robotframework-okw4robot 0.4.0__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.
- robotframework_okw4robot-0.4.0/LICENSE +36 -0
- robotframework_okw4robot-0.4.0/PKG-INFO +275 -0
- robotframework_okw4robot-0.4.0/README.md +208 -0
- robotframework_okw4robot-0.4.0/pyproject.toml +50 -0
- robotframework_okw4robot-0.4.0/setup.cfg +4 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/__init__.py +3 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/keywords/__init__.py +0 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/keywords/app.py +37 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/keywords/attribute_keywords.py +54 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/keywords/caption_keywords.py +53 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/keywords/host.py +35 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/keywords/label_keywords.py +53 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/keywords/list_keywords.py +72 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/keywords/params.py +43 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/keywords/placeholder_keywords.py +53 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/keywords/table_keywords.py +746 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/keywords/tooltip_keywords.py +99 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/keywords/widget_keywords.py +510 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/library.py +159 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/runtime/__init__.py +0 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/runtime/context.py +168 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/utils/__init__.py +0 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/utils/loader.py +6 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/utils/logging_mixin.py +56 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/utils/okw_helpers.py +177 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/utils/table_tokens.py +129 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/utils/yaml_loader.py +59 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/widgets/__init__.py +0 -0
- robotframework_okw4robot-0.4.0/src/okw4robot/widgets/okw_widget.py +144 -0
- robotframework_okw4robot-0.4.0/src/robotframework_okw4robot.egg-info/PKG-INFO +275 -0
- robotframework_okw4robot-0.4.0/src/robotframework_okw4robot.egg-info/SOURCES.txt +32 -0
- robotframework_okw4robot-0.4.0/src/robotframework_okw4robot.egg-info/dependency_links.txt +1 -0
- robotframework_okw4robot-0.4.0/src/robotframework_okw4robot.egg-info/requires.txt +6 -0
- robotframework_okw4robot-0.4.0/src/robotframework_okw4robot.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
OKW4Robot Community License (Nicht‑kommerzielle Nutzung)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Zoltán Hrabovszki
|
|
4
|
+
|
|
5
|
+
1. Begriffe
|
|
6
|
+
- "Software": die Bibliothek "OKW4Robot" samt zugehöriger Quell‑ und Binärdateien.
|
|
7
|
+
- "Nicht‑kommerzielle Nutzung": jede Nutzung zu privaten, akademischen oder Open‑Source‑Zwecken ohne direkten oder indirekten Umsatzbezug, sowie reine Evaluierung/PoC.
|
|
8
|
+
- "Kommerzielle Nutzung": jede Nutzung in oder für ein Produkt, einen Service oder Prozess, der direkt oder indirekt Einnahmen generiert (z. B. bezahlte Projekte, Firmen‑CI/Tests für kommerzielle Produkte, SaaS, Resale). Für kommerzielle Nutzung ist eine gesonderte kommerzielle Lizenz erforderlich.
|
|
9
|
+
|
|
10
|
+
2. Lizenzgewährung (nicht‑kommerziell)
|
|
11
|
+
Der Rechteinhaber gewährt eine weltweite, nicht‑exklusive, nicht übertragbare, unentgeltliche Lizenz zur Nutzung, Vervielfältigung, Änderung und Weitergabe der Software ausschließlich für nicht‑kommerzielle Zwecke, vorausgesetzt, dass (a) dieser Lizenztext in Kopien und abgeleiteten Werken enthalten bleibt und (b) Urheber‑ und Hinweistexte nicht entfernt werden.
|
|
12
|
+
|
|
13
|
+
3. Einschränkungen
|
|
14
|
+
- Kommerzielle Nutzung ist ohne kommerzielle Lizenz untersagt.
|
|
15
|
+
- Marken‑ und Namensrechte werden nicht gewährt.
|
|
16
|
+
- Sublicensing ist nur unter Beibehaltung dieser Bedingungen zulässig.
|
|
17
|
+
|
|
18
|
+
4. Support
|
|
19
|
+
Es besteht kein Anspruch auf Wartung, Support oder Updates.
|
|
20
|
+
|
|
21
|
+
5. Gewährleistungsausschluss
|
|
22
|
+
Die Software wird "wie besehen" ("AS IS") bereitgestellt, ohne ausdrückliche oder stillschweigende Gewährleistung. Insbesondere wird keine Gewähr für Marktgängigkeit, Eignung für einen bestimmten Zweck, Nichtverletzung von Rechten Dritter, Fehlerfreiheit, Richtigkeit oder Verfügbarkeit übernommen.
|
|
23
|
+
|
|
24
|
+
6. Haftungsbeschränkung
|
|
25
|
+
Soweit gesetzlich zulässig, haften der Rechteinhaber und Mitwirkende in keinem Fall für Schäden jedweder Art (einschließlich, aber nicht beschränkt auf direkte, indirekte, zufällige, besondere, exemplarische oder Folgeschäden, entgangenen Gewinn, Produktions‑/Betriebsunterbrechung, Datenverlust oder Rechtsverfolgungskosten), die aus der Nutzung, Unmöglichkeit der Nutzung oder Leistung der Software entstehen, selbst wenn auf die Möglichkeit solcher Schäden hingewiesen wurde.
|
|
26
|
+
|
|
27
|
+
Unberührt bleiben zwingende Haftungstatbestände (z. B. nach dem Produkthaftungsgesetz) sowie die Haftung für Vorsatz, grobe Fahrlässigkeit oder für Verletzungen von Leben, Körper oder Gesundheit nach geltendem Recht.
|
|
28
|
+
|
|
29
|
+
7. Laufzeit und Beendigung
|
|
30
|
+
Diese Lizenz gilt bis zu ihrer Beendigung. Bei Verstoß erlischt das Nutzungsrecht automatisch; die Nutzung ist einzustellen. Rechte an bereits rechtmäßig weitergegebenen Kopien bleiben unberührt.
|
|
31
|
+
|
|
32
|
+
8. Sonstiges
|
|
33
|
+
Es gilt materielles Recht des Sitzstaats des Rechteinhabers unter Ausschluss des Kollisionsrechts. Sollte eine Bestimmung unwirksam sein, bleibt der Rest wirksam (Salvatorische Klausel).
|
|
34
|
+
|
|
35
|
+
Kommerzielle Lizenz
|
|
36
|
+
Für eine kommerzielle Nutzung (siehe Abschnitt 1) wenden Sie sich bitte an den Rechteinhaber, um Konditionen und Gebühren zu vereinbaren.
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: robotframework-okw4robot
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Driver-agnostic Robot Framework keyword library core for GUI test automation (OKW4Robot).
|
|
5
|
+
Author: Zoltán Hrabovszki
|
|
6
|
+
License: OKW4Robot Community License (Nicht‑kommerzielle Nutzung)
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 Zoltán Hrabovszki
|
|
9
|
+
|
|
10
|
+
1. Begriffe
|
|
11
|
+
- "Software": die Bibliothek "OKW4Robot" samt zugehöriger Quell‑ und Binärdateien.
|
|
12
|
+
- "Nicht‑kommerzielle Nutzung": jede Nutzung zu privaten, akademischen oder Open‑Source‑Zwecken ohne direkten oder indirekten Umsatzbezug, sowie reine Evaluierung/PoC.
|
|
13
|
+
- "Kommerzielle Nutzung": jede Nutzung in oder für ein Produkt, einen Service oder Prozess, der direkt oder indirekt Einnahmen generiert (z. B. bezahlte Projekte, Firmen‑CI/Tests für kommerzielle Produkte, SaaS, Resale). Für kommerzielle Nutzung ist eine gesonderte kommerzielle Lizenz erforderlich.
|
|
14
|
+
|
|
15
|
+
2. Lizenzgewährung (nicht‑kommerziell)
|
|
16
|
+
Der Rechteinhaber gewährt eine weltweite, nicht‑exklusive, nicht übertragbare, unentgeltliche Lizenz zur Nutzung, Vervielfältigung, Änderung und Weitergabe der Software ausschließlich für nicht‑kommerzielle Zwecke, vorausgesetzt, dass (a) dieser Lizenztext in Kopien und abgeleiteten Werken enthalten bleibt und (b) Urheber‑ und Hinweistexte nicht entfernt werden.
|
|
17
|
+
|
|
18
|
+
3. Einschränkungen
|
|
19
|
+
- Kommerzielle Nutzung ist ohne kommerzielle Lizenz untersagt.
|
|
20
|
+
- Marken‑ und Namensrechte werden nicht gewährt.
|
|
21
|
+
- Sublicensing ist nur unter Beibehaltung dieser Bedingungen zulässig.
|
|
22
|
+
|
|
23
|
+
4. Support
|
|
24
|
+
Es besteht kein Anspruch auf Wartung, Support oder Updates.
|
|
25
|
+
|
|
26
|
+
5. Gewährleistungsausschluss
|
|
27
|
+
Die Software wird "wie besehen" ("AS IS") bereitgestellt, ohne ausdrückliche oder stillschweigende Gewährleistung. Insbesondere wird keine Gewähr für Marktgängigkeit, Eignung für einen bestimmten Zweck, Nichtverletzung von Rechten Dritter, Fehlerfreiheit, Richtigkeit oder Verfügbarkeit übernommen.
|
|
28
|
+
|
|
29
|
+
6. Haftungsbeschränkung
|
|
30
|
+
Soweit gesetzlich zulässig, haften der Rechteinhaber und Mitwirkende in keinem Fall für Schäden jedweder Art (einschließlich, aber nicht beschränkt auf direkte, indirekte, zufällige, besondere, exemplarische oder Folgeschäden, entgangenen Gewinn, Produktions‑/Betriebsunterbrechung, Datenverlust oder Rechtsverfolgungskosten), die aus der Nutzung, Unmöglichkeit der Nutzung oder Leistung der Software entstehen, selbst wenn auf die Möglichkeit solcher Schäden hingewiesen wurde.
|
|
31
|
+
|
|
32
|
+
Unberührt bleiben zwingende Haftungstatbestände (z. B. nach dem Produkthaftungsgesetz) sowie die Haftung für Vorsatz, grobe Fahrlässigkeit oder für Verletzungen von Leben, Körper oder Gesundheit nach geltendem Recht.
|
|
33
|
+
|
|
34
|
+
7. Laufzeit und Beendigung
|
|
35
|
+
Diese Lizenz gilt bis zu ihrer Beendigung. Bei Verstoß erlischt das Nutzungsrecht automatisch; die Nutzung ist einzustellen. Rechte an bereits rechtmäßig weitergegebenen Kopien bleiben unberührt.
|
|
36
|
+
|
|
37
|
+
8. Sonstiges
|
|
38
|
+
Es gilt materielles Recht des Sitzstaats des Rechteinhabers unter Ausschluss des Kollisionsrechts. Sollte eine Bestimmung unwirksam sein, bleibt der Rest wirksam (Salvatorische Klausel).
|
|
39
|
+
|
|
40
|
+
Kommerzielle Lizenz
|
|
41
|
+
Für eine kommerzielle Nutzung (siehe Abschnitt 1) wenden Sie sich bitte an den Rechteinhaber, um Konditionen und Gebühren zu vereinbaren.
|
|
42
|
+
|
|
43
|
+
Project-URL: Repository, https://github.com/Hrabovszki1023/robotframework-okw4robot
|
|
44
|
+
Project-URL: Homepage, https://github.com/Hrabovszki1023/robotframework-okw4robot
|
|
45
|
+
Project-URL: Documentation, https://hrabovszki1023.github.io/robotframework-okw4robot/
|
|
46
|
+
Keywords: robotframework,testing,testautomation,gui,okw,okw4robot
|
|
47
|
+
Classifier: Development Status :: 4 - Beta
|
|
48
|
+
Classifier: Operating System :: OS Independent
|
|
49
|
+
Classifier: Programming Language :: Python :: 3
|
|
50
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
51
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
52
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
53
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
54
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
55
|
+
Classifier: Framework :: Robot Framework
|
|
56
|
+
Classifier: Framework :: Robot Framework :: Library
|
|
57
|
+
Classifier: Topic :: Software Development :: Testing
|
|
58
|
+
Requires-Python: >=3.10
|
|
59
|
+
Description-Content-Type: text/markdown
|
|
60
|
+
License-File: LICENSE
|
|
61
|
+
Requires-Dist: robotframework>=6.0
|
|
62
|
+
Requires-Dist: PyYAML>=6.0
|
|
63
|
+
Requires-Dist: okw-contract-utils>=0.2.0
|
|
64
|
+
Provides-Extra: dev
|
|
65
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
66
|
+
Dynamic: license-file
|
|
67
|
+
|
|
68
|
+
# OKW4Robot
|
|
69
|
+
|
|
70
|
+
Driver-agnostic keyword architecture for [Robot Framework](https://robotframework.org/).
|
|
71
|
+
|
|
72
|
+
One unified set of keywords for different GUI technologies
|
|
73
|
+
(Web/Selenium, Web/Playwright, Java Swing, ...) — the actual implementation
|
|
74
|
+
lives in the respective driver package.
|
|
75
|
+
|
|
76
|
+
> **Deutsche Version:** [README_de.md](README_de.md)
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## OKW Ecosystem
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
┌─────────────────────────────────────────────────────────┐
|
|
84
|
+
│ Robot Tests (.robot) │
|
|
85
|
+
│ StartHost Chrome / SetValue Name "Smith" │
|
|
86
|
+
└──────────────────────────┬──────────────────────────────┘
|
|
87
|
+
│
|
|
88
|
+
┌───────────▼───────────┐
|
|
89
|
+
│ okw4robot │ Driver-agnostic core
|
|
90
|
+
│ Keywords, OkwWidget │ pip install robotframework-okw4robot
|
|
91
|
+
│ Context, Contracts │
|
|
92
|
+
└───┬───────────────┬───┘
|
|
93
|
+
│ │
|
|
94
|
+
┌──────────▼──┐ ┌─────▼──────────┐
|
|
95
|
+
│ okw-web- │ │ okw-java- │ (more drivers
|
|
96
|
+
│ selenium │ │ swing │ e.g. okw-web-
|
|
97
|
+
│ WebSe_* │ │ JavaSw_* │ playwright)
|
|
98
|
+
└─────────────┘ └────────────────┘
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
| Package | Namespace | Status | Description |
|
|
102
|
+
|---------|-----------|--------|-------------|
|
|
103
|
+
| **okw4robot** | `okw4robot` | Stable | Core: keywords, OkwWidget interface, context, contracts |
|
|
104
|
+
| **okw-web-selenium** | `okw_web_selenium` | Stable (53 tests) | Selenium WebDriver + WebSe_* widgets |
|
|
105
|
+
| **okw-java-swing** | `okw_java_swing` | WIP | Java Swing via JSON-RPC |
|
|
106
|
+
| **okw-contract-utils** | `okw_contract_utils` | Stable (PyPI) | Shared contracts (matchers, tokens, YES/NO) |
|
|
107
|
+
| **okw-remote-ssh** | `robotframework_okw_remote_ssh` | Beta (PyPI) | SSH commands and SFTP |
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Getting Started
|
|
112
|
+
|
|
113
|
+
### 1. Install the core
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pip install robotframework-okw4robot
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 2. Install a driver package (e.g. Selenium)
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install robotframework-okw-web-selenium
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 3. Write a Robot test
|
|
126
|
+
|
|
127
|
+
```robotframework
|
|
128
|
+
*** Settings ***
|
|
129
|
+
Library okw_web_selenium.library.OkwWebSeleniumLibrary
|
|
130
|
+
|
|
131
|
+
*** Test Cases ***
|
|
132
|
+
Login Test
|
|
133
|
+
StartHost Chrome
|
|
134
|
+
StartApp Chrome
|
|
135
|
+
SelectWindow Chrome
|
|
136
|
+
SetValue URL https://example.com/login
|
|
137
|
+
StartApp MyApp
|
|
138
|
+
SelectWindow LoginDialog
|
|
139
|
+
SetValue Username admin
|
|
140
|
+
SetValue Password secret
|
|
141
|
+
ClickOn Login
|
|
142
|
+
VerifyValue Status Logged in
|
|
143
|
+
StopHost
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
> **Note:** The test imports the *driver library* (`OkwWebSeleniumLibrary`),
|
|
147
|
+
> not `OKW4RobotLibrary` directly. The driver library inherits all keywords
|
|
148
|
+
> from the core.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Architecture: Delegation, Not Control
|
|
153
|
+
|
|
154
|
+
Keywords do not call adapter methods directly. Instead, they delegate to
|
|
155
|
+
exactly **one** `okw_*` method on the widget:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
Keyword SetValue "Name" "Smith"
|
|
159
|
+
│
|
|
160
|
+
▼
|
|
161
|
+
widget = resolve_widget("Name") # YAML locator → WebSe_TextField
|
|
162
|
+
widget.okw_set_value("Smith") # Widget knows how.
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The `OkwWidget` interface (in `okw4robot`) defines all `okw_*` methods
|
|
166
|
+
with `NotImplementedError`. Driver packages implement them:
|
|
167
|
+
|
|
168
|
+
| okw4robot (Interface) | okw-web-selenium (Implementation) |
|
|
169
|
+
|-----------------------|-----------------------------------|
|
|
170
|
+
| `OkwWidget.okw_set_value()` | `WebSe_TextField.okw_set_value()` → Selenium `clear` + `input_text` |
|
|
171
|
+
| `OkwWidget.okw_click()` | `WebSe_Base.okw_click()` → Selenium `click_element` |
|
|
172
|
+
| `OkwWidget.okw_exists()` | `WebSe_Base.okw_exists()` → Selenium `find_elements` |
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Project Structure
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
robotframework-okw4robot/
|
|
180
|
+
src/okw4robot/
|
|
181
|
+
library.py # OKW4RobotLibrary (all keyword mixins)
|
|
182
|
+
keywords/
|
|
183
|
+
host.py # StartHost, StopHost, SelectHost
|
|
184
|
+
app.py # StartApp, StopApp, SelectWindow
|
|
185
|
+
widget_keywords.py # SetValue, ClickOn, VerifyValue, ...
|
|
186
|
+
attribute_keywords.py # VerifyAttribute, MemorizeAttribute, LogAttribute
|
|
187
|
+
caption_keywords.py # VerifyCaption, MemorizeCaption, LogCaption
|
|
188
|
+
label_keywords.py # VerifyLabel, MemorizeLabel, LogLabel
|
|
189
|
+
placeholder_keywords.py # VerifyPlaceholder, ...
|
|
190
|
+
tooltip_keywords.py # VerifyTooltip, ...
|
|
191
|
+
table_keywords.py # VerifyTableCellValue, ...
|
|
192
|
+
list_keywords.py # VerifyListCount, VerifySelectedCount
|
|
193
|
+
params.py # SetOKWParameter (timeouts)
|
|
194
|
+
runtime/
|
|
195
|
+
context.py # Central runtime context (adapter, app, window)
|
|
196
|
+
widgets/
|
|
197
|
+
okw_widget.py # OkwWidget interface (NotImplementedError defaults)
|
|
198
|
+
utils/
|
|
199
|
+
yaml_loader.py # YAML locator search (project → driver packages)
|
|
200
|
+
loader.py # Dynamic class loading
|
|
201
|
+
okw_helpers.py # resolve_widget(), should_ignore(), ...
|
|
202
|
+
logging_mixin.py # LoggingMixin for all classes
|
|
203
|
+
table_tokens.py # $TAB/$LF token parser
|
|
204
|
+
tests/
|
|
205
|
+
unit/ # 53 pytest unit tests with MockWidget
|
|
206
|
+
docs/
|
|
207
|
+
CONTRACT.md # Public contract
|
|
208
|
+
KEYWORDS.md # Keyword reference (all keywords)
|
|
209
|
+
SPECIFICATION.md # Semantic specification
|
|
210
|
+
keywords_*.md # Keyword documentation per topic
|
|
211
|
+
okw_parameters.md # Timeouts and parameters
|
|
212
|
+
synchronization_strategy.md # Sync strategy (wait_before)
|
|
213
|
+
...
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Documentation
|
|
219
|
+
|
|
220
|
+
### Contracts and Specification
|
|
221
|
+
|
|
222
|
+
- [CONTRACT.md](docs/CONTRACT.md) – Public contract (architecture, YAML fallback)
|
|
223
|
+
- [KEYWORDS.md](docs/KEYWORDS.md) – Keyword reference (all keywords at a glance)
|
|
224
|
+
- [SPECIFICATION.md](docs/SPECIFICATION.md) – Semantic keyword specification
|
|
225
|
+
|
|
226
|
+
### Keywords
|
|
227
|
+
|
|
228
|
+
- [keywords_host_app.md](docs/keywords_host_app.md) – Host/App/Window keywords
|
|
229
|
+
- [keywords_attribute.md](docs/keywords_attribute.md) – VerifyAttribute, MemorizeAttribute, LogAttribute
|
|
230
|
+
- [keywords_caption.md](docs/keywords_caption.md) – VerifyCaption, MemorizeCaption, LogCaption
|
|
231
|
+
- [keywords_label.md](docs/keywords_label.md) – VerifyLabel, MemorizeLabel, LogLabel
|
|
232
|
+
- [keywords_placeholder.md](docs/keywords_placeholder.md) – VerifyPlaceholder, ...
|
|
233
|
+
- [keywords_tooltip.md](docs/keywords_tooltip.md) – VerifyTooltip, ...
|
|
234
|
+
- [keywords_table.md](docs/keywords_table.md) – Table keywords (index-based)
|
|
235
|
+
- [keywords_table_headers.md](docs/keywords_table_headers.md) – Table keywords (header-based)
|
|
236
|
+
- [keywords_list.md](docs/keywords_list.md) – VerifyListCount, VerifySelectedCount
|
|
237
|
+
- [keywords_ignore_rule.md](docs/keywords_ignore_rule.md) – $IGNORE, $EMPTY, $DELETE
|
|
238
|
+
|
|
239
|
+
### Concepts
|
|
240
|
+
|
|
241
|
+
- [context.md](docs/context.md) – Runtime context (adapter, app, window)
|
|
242
|
+
- [objektzustaende.md](docs/objektzustaende.md) – Widget states (exists, visible, enabled, ...)
|
|
243
|
+
- [okw_parameters.md](docs/okw_parameters.md) – Timeouts and parameters ([DE](docs/okw_parameters_de.md))
|
|
244
|
+
- [synchronization_strategy.md](docs/synchronization_strategy.md) – Sync strategy
|
|
245
|
+
- [widgets_common.md](docs/widgets_common.md) – Widget hierarchy and OkwWidget interface
|
|
246
|
+
- [table_tokens.md](docs/table_tokens.md) – $TAB/$LF token syntax
|
|
247
|
+
- [regex_best_practices.md](docs/regex_best_practices.md) – Regex tips for Robot Framework
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Waiting for Values and Synchronization
|
|
252
|
+
|
|
253
|
+
Verify keywords automatically wait for expected values (with timeout and polling):
|
|
254
|
+
|
|
255
|
+
```robotframework
|
|
256
|
+
# Set timeout (optional, default: 10s)
|
|
257
|
+
SetOKWParameter TimeOutVerifyValue 15
|
|
258
|
+
|
|
259
|
+
# Keyword waits up to 15s for the expected value
|
|
260
|
+
VerifyValue Status Logged in
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Write actions (Click, SetValue, TypeKey, Select) check preconditions first:
|
|
264
|
+
`exists → scroll_into_view → visible → enabled → editable → until_not_visible`
|
|
265
|
+
|
|
266
|
+
Details: [docs/okw_parameters.md](docs/okw_parameters.md),
|
|
267
|
+
[docs/synchronization_strategy.md](docs/synchronization_strategy.md)
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## License
|
|
272
|
+
|
|
273
|
+
- **Community** (non-commercial): see [LICENSE](LICENSE)
|
|
274
|
+
- **Commercial**: see [COMMERCIAL_LICENSE.md](COMMERCIAL_LICENSE.md)
|
|
275
|
+
- **FAQ**: [docs/license_faq.md](docs/license_faq.md)
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# OKW4Robot
|
|
2
|
+
|
|
3
|
+
Driver-agnostic keyword architecture for [Robot Framework](https://robotframework.org/).
|
|
4
|
+
|
|
5
|
+
One unified set of keywords for different GUI technologies
|
|
6
|
+
(Web/Selenium, Web/Playwright, Java Swing, ...) — the actual implementation
|
|
7
|
+
lives in the respective driver package.
|
|
8
|
+
|
|
9
|
+
> **Deutsche Version:** [README_de.md](README_de.md)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## OKW Ecosystem
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
┌─────────────────────────────────────────────────────────┐
|
|
17
|
+
│ Robot Tests (.robot) │
|
|
18
|
+
│ StartHost Chrome / SetValue Name "Smith" │
|
|
19
|
+
└──────────────────────────┬──────────────────────────────┘
|
|
20
|
+
│
|
|
21
|
+
┌───────────▼───────────┐
|
|
22
|
+
│ okw4robot │ Driver-agnostic core
|
|
23
|
+
│ Keywords, OkwWidget │ pip install robotframework-okw4robot
|
|
24
|
+
│ Context, Contracts │
|
|
25
|
+
└───┬───────────────┬───┘
|
|
26
|
+
│ │
|
|
27
|
+
┌──────────▼──┐ ┌─────▼──────────┐
|
|
28
|
+
│ okw-web- │ │ okw-java- │ (more drivers
|
|
29
|
+
│ selenium │ │ swing │ e.g. okw-web-
|
|
30
|
+
│ WebSe_* │ │ JavaSw_* │ playwright)
|
|
31
|
+
└─────────────┘ └────────────────┘
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Package | Namespace | Status | Description |
|
|
35
|
+
|---------|-----------|--------|-------------|
|
|
36
|
+
| **okw4robot** | `okw4robot` | Stable | Core: keywords, OkwWidget interface, context, contracts |
|
|
37
|
+
| **okw-web-selenium** | `okw_web_selenium` | Stable (53 tests) | Selenium WebDriver + WebSe_* widgets |
|
|
38
|
+
| **okw-java-swing** | `okw_java_swing` | WIP | Java Swing via JSON-RPC |
|
|
39
|
+
| **okw-contract-utils** | `okw_contract_utils` | Stable (PyPI) | Shared contracts (matchers, tokens, YES/NO) |
|
|
40
|
+
| **okw-remote-ssh** | `robotframework_okw_remote_ssh` | Beta (PyPI) | SSH commands and SFTP |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Getting Started
|
|
45
|
+
|
|
46
|
+
### 1. Install the core
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install robotframework-okw4robot
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 2. Install a driver package (e.g. Selenium)
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install robotframework-okw-web-selenium
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 3. Write a Robot test
|
|
59
|
+
|
|
60
|
+
```robotframework
|
|
61
|
+
*** Settings ***
|
|
62
|
+
Library okw_web_selenium.library.OkwWebSeleniumLibrary
|
|
63
|
+
|
|
64
|
+
*** Test Cases ***
|
|
65
|
+
Login Test
|
|
66
|
+
StartHost Chrome
|
|
67
|
+
StartApp Chrome
|
|
68
|
+
SelectWindow Chrome
|
|
69
|
+
SetValue URL https://example.com/login
|
|
70
|
+
StartApp MyApp
|
|
71
|
+
SelectWindow LoginDialog
|
|
72
|
+
SetValue Username admin
|
|
73
|
+
SetValue Password secret
|
|
74
|
+
ClickOn Login
|
|
75
|
+
VerifyValue Status Logged in
|
|
76
|
+
StopHost
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
> **Note:** The test imports the *driver library* (`OkwWebSeleniumLibrary`),
|
|
80
|
+
> not `OKW4RobotLibrary` directly. The driver library inherits all keywords
|
|
81
|
+
> from the core.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Architecture: Delegation, Not Control
|
|
86
|
+
|
|
87
|
+
Keywords do not call adapter methods directly. Instead, they delegate to
|
|
88
|
+
exactly **one** `okw_*` method on the widget:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
Keyword SetValue "Name" "Smith"
|
|
92
|
+
│
|
|
93
|
+
▼
|
|
94
|
+
widget = resolve_widget("Name") # YAML locator → WebSe_TextField
|
|
95
|
+
widget.okw_set_value("Smith") # Widget knows how.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The `OkwWidget` interface (in `okw4robot`) defines all `okw_*` methods
|
|
99
|
+
with `NotImplementedError`. Driver packages implement them:
|
|
100
|
+
|
|
101
|
+
| okw4robot (Interface) | okw-web-selenium (Implementation) |
|
|
102
|
+
|-----------------------|-----------------------------------|
|
|
103
|
+
| `OkwWidget.okw_set_value()` | `WebSe_TextField.okw_set_value()` → Selenium `clear` + `input_text` |
|
|
104
|
+
| `OkwWidget.okw_click()` | `WebSe_Base.okw_click()` → Selenium `click_element` |
|
|
105
|
+
| `OkwWidget.okw_exists()` | `WebSe_Base.okw_exists()` → Selenium `find_elements` |
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Project Structure
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
robotframework-okw4robot/
|
|
113
|
+
src/okw4robot/
|
|
114
|
+
library.py # OKW4RobotLibrary (all keyword mixins)
|
|
115
|
+
keywords/
|
|
116
|
+
host.py # StartHost, StopHost, SelectHost
|
|
117
|
+
app.py # StartApp, StopApp, SelectWindow
|
|
118
|
+
widget_keywords.py # SetValue, ClickOn, VerifyValue, ...
|
|
119
|
+
attribute_keywords.py # VerifyAttribute, MemorizeAttribute, LogAttribute
|
|
120
|
+
caption_keywords.py # VerifyCaption, MemorizeCaption, LogCaption
|
|
121
|
+
label_keywords.py # VerifyLabel, MemorizeLabel, LogLabel
|
|
122
|
+
placeholder_keywords.py # VerifyPlaceholder, ...
|
|
123
|
+
tooltip_keywords.py # VerifyTooltip, ...
|
|
124
|
+
table_keywords.py # VerifyTableCellValue, ...
|
|
125
|
+
list_keywords.py # VerifyListCount, VerifySelectedCount
|
|
126
|
+
params.py # SetOKWParameter (timeouts)
|
|
127
|
+
runtime/
|
|
128
|
+
context.py # Central runtime context (adapter, app, window)
|
|
129
|
+
widgets/
|
|
130
|
+
okw_widget.py # OkwWidget interface (NotImplementedError defaults)
|
|
131
|
+
utils/
|
|
132
|
+
yaml_loader.py # YAML locator search (project → driver packages)
|
|
133
|
+
loader.py # Dynamic class loading
|
|
134
|
+
okw_helpers.py # resolve_widget(), should_ignore(), ...
|
|
135
|
+
logging_mixin.py # LoggingMixin for all classes
|
|
136
|
+
table_tokens.py # $TAB/$LF token parser
|
|
137
|
+
tests/
|
|
138
|
+
unit/ # 53 pytest unit tests with MockWidget
|
|
139
|
+
docs/
|
|
140
|
+
CONTRACT.md # Public contract
|
|
141
|
+
KEYWORDS.md # Keyword reference (all keywords)
|
|
142
|
+
SPECIFICATION.md # Semantic specification
|
|
143
|
+
keywords_*.md # Keyword documentation per topic
|
|
144
|
+
okw_parameters.md # Timeouts and parameters
|
|
145
|
+
synchronization_strategy.md # Sync strategy (wait_before)
|
|
146
|
+
...
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Documentation
|
|
152
|
+
|
|
153
|
+
### Contracts and Specification
|
|
154
|
+
|
|
155
|
+
- [CONTRACT.md](docs/CONTRACT.md) – Public contract (architecture, YAML fallback)
|
|
156
|
+
- [KEYWORDS.md](docs/KEYWORDS.md) – Keyword reference (all keywords at a glance)
|
|
157
|
+
- [SPECIFICATION.md](docs/SPECIFICATION.md) – Semantic keyword specification
|
|
158
|
+
|
|
159
|
+
### Keywords
|
|
160
|
+
|
|
161
|
+
- [keywords_host_app.md](docs/keywords_host_app.md) – Host/App/Window keywords
|
|
162
|
+
- [keywords_attribute.md](docs/keywords_attribute.md) – VerifyAttribute, MemorizeAttribute, LogAttribute
|
|
163
|
+
- [keywords_caption.md](docs/keywords_caption.md) – VerifyCaption, MemorizeCaption, LogCaption
|
|
164
|
+
- [keywords_label.md](docs/keywords_label.md) – VerifyLabel, MemorizeLabel, LogLabel
|
|
165
|
+
- [keywords_placeholder.md](docs/keywords_placeholder.md) – VerifyPlaceholder, ...
|
|
166
|
+
- [keywords_tooltip.md](docs/keywords_tooltip.md) – VerifyTooltip, ...
|
|
167
|
+
- [keywords_table.md](docs/keywords_table.md) – Table keywords (index-based)
|
|
168
|
+
- [keywords_table_headers.md](docs/keywords_table_headers.md) – Table keywords (header-based)
|
|
169
|
+
- [keywords_list.md](docs/keywords_list.md) – VerifyListCount, VerifySelectedCount
|
|
170
|
+
- [keywords_ignore_rule.md](docs/keywords_ignore_rule.md) – $IGNORE, $EMPTY, $DELETE
|
|
171
|
+
|
|
172
|
+
### Concepts
|
|
173
|
+
|
|
174
|
+
- [context.md](docs/context.md) – Runtime context (adapter, app, window)
|
|
175
|
+
- [objektzustaende.md](docs/objektzustaende.md) – Widget states (exists, visible, enabled, ...)
|
|
176
|
+
- [okw_parameters.md](docs/okw_parameters.md) – Timeouts and parameters ([DE](docs/okw_parameters_de.md))
|
|
177
|
+
- [synchronization_strategy.md](docs/synchronization_strategy.md) – Sync strategy
|
|
178
|
+
- [widgets_common.md](docs/widgets_common.md) – Widget hierarchy and OkwWidget interface
|
|
179
|
+
- [table_tokens.md](docs/table_tokens.md) – $TAB/$LF token syntax
|
|
180
|
+
- [regex_best_practices.md](docs/regex_best_practices.md) – Regex tips for Robot Framework
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Waiting for Values and Synchronization
|
|
185
|
+
|
|
186
|
+
Verify keywords automatically wait for expected values (with timeout and polling):
|
|
187
|
+
|
|
188
|
+
```robotframework
|
|
189
|
+
# Set timeout (optional, default: 10s)
|
|
190
|
+
SetOKWParameter TimeOutVerifyValue 15
|
|
191
|
+
|
|
192
|
+
# Keyword waits up to 15s for the expected value
|
|
193
|
+
VerifyValue Status Logged in
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Write actions (Click, SetValue, TypeKey, Select) check preconditions first:
|
|
197
|
+
`exists → scroll_into_view → visible → enabled → editable → until_not_visible`
|
|
198
|
+
|
|
199
|
+
Details: [docs/okw_parameters.md](docs/okw_parameters.md),
|
|
200
|
+
[docs/synchronization_strategy.md](docs/synchronization_strategy.md)
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
- **Community** (non-commercial): see [LICENSE](LICENSE)
|
|
207
|
+
- **Commercial**: see [COMMERCIAL_LICENSE.md](COMMERCIAL_LICENSE.md)
|
|
208
|
+
- **FAQ**: [docs/license_faq.md](docs/license_faq.md)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=65", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "robotframework-okw4robot"
|
|
7
|
+
version = "0.4.0"
|
|
8
|
+
description = "Driver-agnostic Robot Framework keyword library core for GUI test automation (OKW4Robot)."
|
|
9
|
+
authors = [{ name = "Zoltán Hrabovszki" }]
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
keywords = ["robotframework", "testing", "testautomation", "gui", "okw", "okw4robot"]
|
|
14
|
+
dependencies = [
|
|
15
|
+
"robotframework>=6.0",
|
|
16
|
+
"PyYAML>=6.0",
|
|
17
|
+
"okw-contract-utils>=0.2.0",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Framework :: Robot Framework",
|
|
29
|
+
"Framework :: Robot Framework :: Library",
|
|
30
|
+
"Topic :: Software Development :: Testing",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Repository = "https://github.com/Hrabovszki1023/robotframework-okw4robot"
|
|
35
|
+
Homepage = "https://github.com/Hrabovszki1023/robotframework-okw4robot"
|
|
36
|
+
Documentation = "https://hrabovszki1023.github.io/robotframework-okw4robot/"
|
|
37
|
+
|
|
38
|
+
[tool.setuptools]
|
|
39
|
+
package-dir = {"" = "src"}
|
|
40
|
+
include-package-data = true
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.packages.find]
|
|
43
|
+
where = ["src"]
|
|
44
|
+
namespaces = false
|
|
45
|
+
|
|
46
|
+
[project.optional-dependencies]
|
|
47
|
+
dev = ["pytest>=7.0"]
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
testpaths = ["tests"]
|
|
File without changes
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from robot.api.deco import keyword
|
|
2
|
+
from ..runtime.context import context
|
|
3
|
+
from ..utils.yaml_loader import load_yaml_with_fallback
|
|
4
|
+
from ..utils.logging_mixin import LoggingMixin
|
|
5
|
+
|
|
6
|
+
class AppKeywords(LoggingMixin):
|
|
7
|
+
|
|
8
|
+
@keyword("StartApp")
|
|
9
|
+
def start_app(self, name: str):
|
|
10
|
+
self.log_info(f"Starte App '{name}'...")
|
|
11
|
+
model = load_yaml_with_fallback(name)
|
|
12
|
+
app_name = name.rsplit("/", 1)[-1]
|
|
13
|
+
|
|
14
|
+
if app_name not in model:
|
|
15
|
+
self.log_error(f"App name '{app_name}' not found in YAML root.")
|
|
16
|
+
raise KeyError(f"App name '{app_name}' not found in YAML root")
|
|
17
|
+
|
|
18
|
+
app_model = model[app_name]
|
|
19
|
+
context.set_app(app_name, app_model)
|
|
20
|
+
self.log_info(f"App '{app_name}' gestartet.")
|
|
21
|
+
|
|
22
|
+
@keyword("SelectWindow")
|
|
23
|
+
def select_window(self, name: str):
|
|
24
|
+
self.log_info(f"Wähle Fenster/Widget '{name}'...")
|
|
25
|
+
context.set_window(name)
|
|
26
|
+
self.log_info(f"Fenster/Widget '{name}' aktiviert.")
|
|
27
|
+
|
|
28
|
+
@keyword("StopApp")
|
|
29
|
+
def stop_app(self):
|
|
30
|
+
if context._app_model is None:
|
|
31
|
+
self.log_error("Keine App aktiv – nichts zu stoppen.")
|
|
32
|
+
raise RuntimeError("Stop App failed: No app is currently active.")
|
|
33
|
+
|
|
34
|
+
self.log_info(f"Beende App '{context._app_name}'.")
|
|
35
|
+
context.stop_app()
|
|
36
|
+
|
|
37
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from robot.api.deco import keyword
|
|
2
|
+
from robot.api import logger
|
|
3
|
+
from ..utils.okw_helpers import should_ignore, get_robot_timeout, resolve_widget, verify_with_timeout, normalize_var_name
|
|
4
|
+
from okw_contract_utils import MatchMode
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _get_attr(w, attr_name: str) -> str:
|
|
8
|
+
try:
|
|
9
|
+
val = w.okw_get_attribute(attr_name)
|
|
10
|
+
return "" if val is None else str(val)
|
|
11
|
+
except Exception:
|
|
12
|
+
return ""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AttributeKeywords:
|
|
16
|
+
@keyword("VerifyAttribute")
|
|
17
|
+
def verify_attribute(self, name, attribute, expected):
|
|
18
|
+
if should_ignore(expected):
|
|
19
|
+
logger.info(f"[VerifyAttribute] '{name}' ignored ($IGNORE)")
|
|
20
|
+
return
|
|
21
|
+
w = resolve_widget(name)
|
|
22
|
+
timeout = get_robot_timeout("${OKW_TIMEOUT_VERIFY_ATTRIBUTE}", 10.0)
|
|
23
|
+
verify_with_timeout(lambda: _get_attr(w, attribute), expected, MatchMode.EXACT, timeout, f"[VerifyAttribute] '{name}'")
|
|
24
|
+
|
|
25
|
+
@keyword("VerifyAttributeWCM")
|
|
26
|
+
def verify_attribute_wcm(self, name, attribute, expected):
|
|
27
|
+
if should_ignore(expected):
|
|
28
|
+
logger.info(f"[VerifyAttributeWCM] '{name}' ignored ($IGNORE)")
|
|
29
|
+
return
|
|
30
|
+
w = resolve_widget(name)
|
|
31
|
+
timeout = get_robot_timeout("${OKW_TIMEOUT_VERIFY_ATTRIBUTE}", 10.0)
|
|
32
|
+
verify_with_timeout(lambda: _get_attr(w, attribute), expected, MatchMode.WCM, timeout, f"[VerifyAttributeWCM] '{name}'")
|
|
33
|
+
|
|
34
|
+
@keyword("VerifyAttributeREGX")
|
|
35
|
+
def verify_attribute_regx(self, name, attribute, expected):
|
|
36
|
+
if should_ignore(expected):
|
|
37
|
+
logger.info(f"[VerifyAttributeREGX] '{name}' ignored ($IGNORE)")
|
|
38
|
+
return
|
|
39
|
+
w = resolve_widget(name)
|
|
40
|
+
timeout = get_robot_timeout("${OKW_TIMEOUT_VERIFY_ATTRIBUTE}", 10.0)
|
|
41
|
+
verify_with_timeout(lambda: _get_attr(w, attribute), expected, MatchMode.REGX, timeout, f"[VerifyAttributeREGX] '{name}'")
|
|
42
|
+
|
|
43
|
+
@keyword("MemorizeAttribute")
|
|
44
|
+
def memorize_attribute(self, name, attribute, variable):
|
|
45
|
+
from robot.libraries.BuiltIn import BuiltIn
|
|
46
|
+
w = resolve_widget(name)
|
|
47
|
+
value = _get_attr(w, attribute)
|
|
48
|
+
BuiltIn().set_test_variable(normalize_var_name(variable), value)
|
|
49
|
+
|
|
50
|
+
@keyword("LogAttribute")
|
|
51
|
+
def log_attribute(self, name, attribute):
|
|
52
|
+
w = resolve_widget(name)
|
|
53
|
+
value = _get_attr(w, attribute)
|
|
54
|
+
logger.info(f"[LogAttribute] {value}")
|