├── ListenerApi2Events.py ├── README.md ├── index.html ├── requirements.txt └── screenshot.png /ListenerApi2Events.py: -------------------------------------------------------------------------------- 1 | from websocket import create_connection 2 | import json 3 | 4 | 5 | class ListenerApi2Events(): 6 | ROBOT_LISTENER_API_VERSION = 2 7 | 8 | def send_message(self, event, name=None, attrs=None, message=None, path=None): 9 | ws = create_connection("ws://localhost:5678/") 10 | info = {} 11 | info["event"] = event 12 | if name: 13 | info["name"] = name 14 | if path: 15 | info["path"] = path 16 | if message: 17 | info.update(message) 18 | if attrs: 19 | info.update(attrs) 20 | ws.send(json.dumps(info)) 21 | ws.close() 22 | 23 | def start_suite(self, name, attrs): 24 | self.send_message("start_suite", name, attrs) 25 | 26 | def end_suite(self, name, attrs): 27 | self.send_message("end_suite", name, attrs) 28 | 29 | def start_test(self, name, attrs): 30 | self.send_message("start_test", name, attrs) 31 | 32 | def end_test(self, name, attrs): 33 | self.send_message("end_test", name, attrs) 34 | 35 | def start_keyword(self, name, attrs): 36 | self.send_message("start_keyword", name, attrs) 37 | 38 | def end_keyword(self, name, attrs): 39 | self.send_message("end_keyword", name, attrs) 40 | 41 | def log_message(self, message): 42 | self.send_message("log_message", message=message) 43 | 44 | def message(self, message): 45 | self.send_message("message", message=message) 46 | 47 | def library_import(self, name, attrs): 48 | self.send_message("library_import", name, attrs) 49 | 50 | def resource_import(self, name, attrs): 51 | self.send_message("resource_import", name, attrs) 52 | 53 | def variables_import(self, name, attrs): 54 | self.send_message("variables_import", name, attrs) 55 | 56 | def output_file(self, path): 57 | self.send_message("output_file", path=path) 58 | 59 | def log_file(self, path): 60 | self.send_message("log_file", path=path) 61 | 62 | def report_file(self, path): 63 | self.send_message("report_file", path=path) 64 | 65 | def xunit_file(self, path): 66 | self.send_message("xunit_file", path=path) 67 | 68 | def debug_file(self, path): 69 | self.send_message("debug_file", path=path) 70 | 71 | def close(self): 72 | self.send_message("close") 73 | 74 | 75 | if __name__ == "__main__": 76 | from SimpleWebSocketServer import SimpleWebSocketServer, WebSocket 77 | 78 | clients = [] 79 | class SimpleServ(WebSocket): 80 | 81 | def handleMessage(self): 82 | for client in clients: 83 | if client != self and not client.closed: 84 | client.sendMessage(self.data) 85 | 86 | def handleConnected(self): 87 | clients.append(self) 88 | 89 | def handleClose(self): 90 | clients.remove(self) 91 | 92 | server = SimpleWebSocketServer('', 5678, SimpleServ) 93 | server.serveforever() 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # robotframework-listener-api-events 2 | 3 | Robot Framework listener for sending JSON formatted events 4 | over websocket. 5 | 6 | This solution can be used for live monitoring and debubbing of 7 | events provided by Robot Framework listener API. Locally running 8 | websocket server receives the event data as JSON and provides it 9 | to websocket clients. 10 | 11 | Initial use case is to provide easier means for debugging and studying the events and 12 | the data available via listener API (version 2). An example web application receives 13 | the data as JSON, parses it and provides an initial user interface. Some of the more 14 | verbose events (`message`, `log_message`) can be hidden from the view. 15 | 16 |  17 | 18 | # Installing and running 19 | 20 | ## Install Python dependencies 21 | 22 | pip install -r requirements.txt 23 | 24 | ## run websocket server on background 25 | 26 | python ListenerApi2Events.py & 27 | 28 | ## open [index.html](./index.html) in web brower, e.g.: 29 | 30 | open index.html 31 | 32 | ## Run Robot Framework with the listener 33 | 34 | robot --listener ListenerApi2Events.py ~/Robots/Simple/tasks/robot.robot 35 | 36 | # Implementation notes 37 | 38 | All methods by Listener API 2 are provided. Arguments are collected to single JSON object 39 | where `"event"` matches the listener API method: 40 | 41 | "event": "end_suite", 42 | 43 | Arguments of the methods (name, attrs, message, path) are added to the JSON object 44 | depending on the availability: 45 | 46 | { 47 | "event": "output_file", 48 | "path": "/Users/user123/src/websocket/output.xml" 49 | } 50 | 51 | { 52 | "event": "start_keyword", 53 | "name": "BuiltIn.Log", 54 | "kwname": "Log", 55 | "libname": "BuiltIn", 56 | "doc": "Logs the given message with the given level.", 57 | "assign": [], 58 | "tags": [], 59 | "starttime": "20200906 23:26:48.209", 60 | "args": [ 61 | "Hello!" 62 | ], 63 | "type": "Keyword" 64 | } 65 | 66 | JSON data can be parsed on web application Javascript as follows: 67 | 68 | var jsondata = JSON.parse(event.data); 69 | 70 | and accessed easily: 71 | 72 | if (jsondata.event == "start_suite") { 73 | button.classList.add("font-weight-bold"); 74 | } 75 | 76 | Using hard coded `ws://localhost:5678/` for now, change in the code if needed. 77 | 78 | 79 | # References 80 | 81 | Robot Framework listeners: https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#listener-interface 82 | 83 | Listener API version 2: https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#listener-version-2 84 | 85 | Based on the reference at https://github.com/mkorpela/robotframework-websocket 86 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |