Skip to content

Commit 7793f1f

Browse files
committed
doc: improve documentaiton about listeters
1 parent ebc69a8 commit 7793f1f

6 files changed

Lines changed: 104 additions & 9 deletions

File tree

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
python-version: [3.10.19, 3.14]
17+
python-version: ["3.10", "3.14"]
1818
rf-version: [6.1.1, 7.4.2]
1919

2020
steps:

README.md

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,15 +156,63 @@ Then Library can be imported in Robot Framework side like this:
156156
Library ${CURDIR}/PluginLib.py plugins=${CURDIR}/MyPlugin.py
157157
```
158158

159+
# Listener
160+
161+
PLC supports
162+
[library listeners](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#libraries-as-listeners),
163+
also listener can be defined in the class that defines keywords. PLC will automatically detect
164+
is class is also listener and set the `ROBOT_LIBRARY_LISTENER` as a list. List will contains all
165+
the class instances that are marked as listeners.
166+
167+
Example:
168+
```robotframework
169+
from robot.running.model import TestCase
170+
from robot.result.model import TestCase as TestCaseResult
171+
172+
from robotlibcore import DynamicCore, keyword
173+
174+
175+
class ListenerExample(DynamicCore):
176+
177+
ROBOT_LIBRARY_SCOPE = 'GLOBAL'
178+
179+
def __init__(self):
180+
self.ROBOT_LIBRARY_LISTENER = self
181+
components = [KeywordsWithListener()]
182+
super().__init__(components)
183+
184+
185+
186+
class KeywordsWithListener:
187+
ROBOT_LISTENER_API_VERSION = 3
188+
189+
def __init__(self):
190+
self.test = None
191+
192+
193+
def start_test(self, data: TestCase, result: TestCaseResult):
194+
self.test = data.name
195+
self.passed = result.passed
196+
197+
@keyword
198+
def keyword_with_listener(self, name: str, status: bool):
199+
assert name == self.test, f"Test case name {name} does not match expected {self.test}"
200+
assert status == self.passed, f"Test case status {status} does not match expected {self.passed} {type(self.passed)}"
201+
202+
```
203+
204+
In the example `KeywordsWithListener` will act as a listener and `start_test` method is
205+
called each time test starts.
206+
159207
# Translation
160208

161-
PLC supports translation of keywords names and documentation. Translations must be provided in
162-
the `translation` argument in the `HybridCore` or `DynamicCore` `__init__`, either as a
163-
dictionary or through a [Path](https://docs.python.org/3/library/pathlib.html) to a
164-
[JSON](https://www.json.org/json-en.html) file. Providing translation data is optional, also it
209+
PLC supports translation of keywords names and documentation. Translations must be provided in
210+
the `translation` argument in the `HybridCore` or `DynamicCore` `__init__`, either as a
211+
dictionary or through a [Path](https://docs.python.org/3/library/pathlib.html) to a
212+
[JSON](https://www.json.org/json-en.html) file. Providing translation data is optional, also it
165213
is not mandatory to provide translation to all keyword.
166214

167-
The keys of the dictionary are the methods names, not the keyword names, which implements keyword.
215+
The keys of the dictionary are the methods names, not the keyword names, which implements keyword.
168216
Values are objects which contains two keys: `name` and `doc`. `name` key contains the keyword
169217
translated name and `doc` contains keyword translated documentation. Providing
170218
`doc` and `name` is optional, i.e. translations data can also provide translations only

atest/ListenerCore.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ def _start_suite(self, name, attrs):
3636

3737
@keyword
3838
def first_component(self, arg: str):
39-
assert arg == self.suite_name, f"Suite name '{self.suite_name}' should be detected by listener, but was not."
39+
name = self.suite_name
40+
assert name == arg, f"Test suite name {name} does not match expected {arg}."
4041

4142

4243
class SecondComponent:
@@ -46,7 +47,9 @@ def __init__(self) -> None:
4647

4748
@keyword
4849
def second_component(self, arg: str):
49-
assert self.listener.test.name == arg, "Test case name should be detected by listener, but was not."
50+
name = self.listener.test.name
51+
assert name == arg, f"Test case name {name} does not match expected {arg}."
52+
5053

5154

5255
class ExternalListener:

atest/ListenerExample.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from robot.running.model import TestCase
2+
from robot.result.model import TestCase as TestCaseResult
3+
4+
from robotlibcore import DynamicCore, keyword
5+
6+
7+
class ListenerExample(DynamicCore):
8+
9+
ROBOT_LIBRARY_SCOPE = 'GLOBAL'
10+
11+
def __init__(self):
12+
self.ROBOT_LIBRARY_LISTENER = self
13+
components = [KeywordsWithListener()]
14+
super().__init__(components)
15+
16+
17+
18+
class KeywordsWithListener:
19+
ROBOT_LISTENER_API_VERSION = 3
20+
21+
def __init__(self):
22+
self.test = None
23+
24+
25+
def start_test(self, data: TestCase, result: TestCaseResult):
26+
self.test = data.name
27+
self.passed = result.passed
28+
29+
@keyword
30+
def keyword_with_listener(self, name: str, status: bool):
31+
assert name == self.test, f"Test case name {name} does not match expected {self.test}"
32+
assert status == self.passed, f"Test case status {status} does not match expected {self.passed} {type(self.passed)}"

atest/run.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
tests = join(curdir, "tests.robot")
1818
tests_types = join(curdir, "tests_types.robot")
1919
plugin_api = join(curdir, "plugin_api")
20+
listener_api = join(curdir, "tests_listener.robot")
2021
sys.path.insert(0, join(curdir, "..", "src"))
2122
python_version = platform.python_version()
2223
for variant in library_variants:
@@ -58,6 +59,13 @@
5859
if rc > 250:
5960
sys.exit(rc)
6061
process_output(output)
62+
63+
output = join(outdir, f"lib-Listener-python-{python_version}-robot-{RF_VERSION}.xml")
64+
rc = run(listener_api, name="Listener", output=output, report=None, log=None, loglevel="debug")
65+
if rc > 250:
66+
sys.exit(rc)
67+
process_output(output)
68+
6169
print("\nCombining results.")
6270
library_variants.append("DynamicTypesLibrary")
6371
xml_files = [str(xml_file) for xml_file in Path(outdir).glob("*.xml")]

atest/tests_listener.robot

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
*** Settings ***
22
Library ListenerCore.py
3+
Library ListenerExample.py
34

45

56
*** Test Cases ***
@@ -22,4 +23,7 @@ Tests The Suite Name
2223
... to the suite name from _start_suite.
2324
...
2425
... It uses an independent class as listener which is manually set.
25-
First Component Tests Listener
26+
First Component Listener
27+
28+
Test With Listener Example
29+
Keyword With Listener Test With Listener Example False

0 commit comments

Comments
 (0)