class PlayBack: def __init__(self, file_manager): self.fileManager = file_manager self.log = Logger('PlayBack') self.traces = [] self.hostKeys = [] self.keyboardListener = KeyboardListener(on_press=self.onPress, on_release=self.onRelease) self.stopped = False self.interval = 0 self.file = '' self.alert = Alert('RPTool - Playback') self.setting = Settings('rptool.cfg') self.verifier = Verifier() def onPress(self, *args): pass def onRelease(self, *args): # Stop recording when press 'esc' if args[0] == Key.esc: self.keyboardListener.stop() self.stopped = True self.alert.notify('[ESC] Playback stopped') return False # Stop the loop execution if args[0] == Key.f3: self.alert.notify('[F3] Loop stopped') self.setting.saveConfig({'loop': '0'}) def traceBack(self): for trace in self.traces: if not self.stopped: if trace.getType() == 'click': self.click(trace) elif trace.getType() == 'press': self.press(trace) elif trace.getType() == 'scroll': self.scroll(trace) elif trace.getType() == 'drag': self.drag(trace) elif trace.getType() == 'checkpoint': self.checkpoint(trace) elif trace.getType() == 'find_and_click': print('type find and click') self.findAndClick(trace) else: pass else: print('Stopped playback') return def findAndClick(self, trace): try: self.alert.notify('wating finding image') location = pyautogui.locateOnScreen(trace.getImage()) pyautogui.click(location) except: self.alert.notify('Image not found') def checkpoint(self, trace): self.verifier.check(trace.getCheckPoint()) def drag(self, trace): x, y = trace.getDrag() pyautogui.dragTo(x, y, 1, button='left') # TODO :: Need work here to Fix ALT+TAB ... def checkAltTab(self): if len(self.hostKeys) > 2: pyautogui.hotkey(self.hostKeys[0], self.hostKeys[1]) self.hostKeys = self.hostKeys[2:] def press(self, trace): if trace.isHotKey(): # TODO :: Need work here to Fix ALT+TAB ... self.hostKeys.append(trace.getKey()) self.checkAltTab() else: if len(self.hostKeys) > 0: self.hostKeys.append(trace.getKey()) print(self.hostKeys) if len(self.hostKeys) == 2: pyautogui.hotkey(self.hostKeys[0], self.hostKeys[1]) if len(self.hostKeys) == 3: pyautogui.hotkey(self.hostKeys[0], self.hostKeys[1], self.hostKeys[2]) self.hostKeys = [] else: key = trace.getKey() if key in ['space', 'backspace', 'enter', 'caps_lock', 'esc']: pyautogui.press(key) else: pyautogui.typewrite(key, 0.1) def click(self, trace): pyautogui.moveTo(trace.getX(), trace.getY(), trace.getTime()) pyautogui.click() def scroll(self, trace): pyautogui.scroll(trace.getDY()) def play(self, file): self.keyboardListener.start() self.traceReader(file) self.traceBack() def traceReader(self, file): try: with open(file) as traces: for rawTrace in traces: trace = Trace(rawTrace) self.traces.append(trace) except FileNotFoundError as err: self.log.debug('Error {}'.format(err))
class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(676, 456) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) self.tabWidget.setGeometry(QtCore.QRect(10, 10, 661, 401)) self.tabWidget.setObjectName("tabWidget") self.tabRPTool = QtWidgets.QWidget() self.tabRPTool.setObjectName("tabRPTool") # List Widget Test Cases self.lvTestCases = QtWidgets.QListWidget(self.tabRPTool) self.lvTestCases.setGeometry(QtCore.QRect(10, 60, 641, 271)) self.lvTestCases.setObjectName("lvTestCases") self.lvTestCases.itemClicked.connect(self.itemSelected) # Button 'rec' self.pbRec = QtWidgets.QPushButton(self.tabRPTool) self.pbRec.setGeometry(QtCore.QRect(10, 340, 41, 25)) self.pbRec.setObjectName("pbRec") self.pbRec.clicked.connect(self.addTestCase) self.leTestCaseName = QtWidgets.QLineEdit(self.tabRPTool) self.leTestCaseName.setGeometry(QtCore.QRect(10, 30, 301, 25)) self.leTestCaseName.setObjectName("leTestCaseName") self.leTestFolder = QtWidgets.QLineEdit(self.tabRPTool) self.leTestFolder.setGeometry(QtCore.QRect(320, 30, 331, 25)) self.leTestFolder.setObjectName("leTestFolder") self.lblTestCaseName = QtWidgets.QLabel(self.tabRPTool) self.lblTestCaseName.setGeometry(QtCore.QRect(10, 10, 121, 17)) self.lblTestCaseName.setObjectName("lblTestCaseName") self.lblTestFolder = QtWidgets.QLabel(self.tabRPTool) self.lblTestFolder.setGeometry(QtCore.QRect(320, 10, 71, 17)) self.lblTestFolder.setObjectName("lblTestFolder") self.pbPlay = QtWidgets.QPushButton(self.tabRPTool) self.pbPlay.setGeometry(QtCore.QRect(60, 340, 51, 25)) self.pbPlay.setObjectName("pbPlay") self.pbPlay.setEnabled(False) self.pbPlay.clicked.connect(self.playTestCase) self.pbDelete = QtWidgets.QPushButton(self.tabRPTool) self.pbDelete.setGeometry(QtCore.QRect(180, 340, 51, 25)) self.pbDelete.setObjectName("pbDelete") self.pbDelete.setEnabled(False) self.pbDelete.clicked.connect(self.deleteTestCase) self.pbPlayAll = QtWidgets.QPushButton(self.tabRPTool) self.pbPlayAll.setGeometry(QtCore.QRect(120, 340, 51, 25)) self.pbPlayAll.setObjectName("pbPlayAll") self.pbPlayAll.setEnabled(False) self.lblToolStatus = QtWidgets.QLabel(self.tabRPTool) self.lblToolStatus.setGeometry(QtCore.QRect(250, 330, 371, 41)) self.lblToolStatus.setObjectName("lblToolStatus") # Loop Tests self.chboxLoop = QtWidgets.QCheckBox(self.tabRPTool) self.chboxLoop.setGeometry(QtCore.QRect(510, 340, 61, 21)) self.chboxLoop.stateChanged.connect(self.changeLoopState) self.chboxLoop.setObjectName("chboxLoop") self.leInterval = QtWidgets.QLineEdit(self.tabRPTool) self.leInterval.setGeometry(QtCore.QRect(580, 340, 71, 25)) self.leInterval.setObjectName("leInterval") self.leInterval.setText('0') self.tabWidget.addTab(self.tabRPTool, "") self.tabSettings = QtWidgets.QWidget() self.tabSettings.setObjectName("tabSettings") # Save settings self.pbSaveSettings = QtWidgets.QPushButton(self.tabSettings) self.pbSaveSettings.setGeometry(QtCore.QRect(10, 330, 61, 25)) self.pbSaveSettings.setObjectName("pbSaveSettings") self.pbSaveSettings.clicked.connect(self.saveSettings) self.lblUsername = QtWidgets.QLabel(self.tabSettings) self.lblUsername.setGeometry(QtCore.QRect(10, 0, 81, 31)) self.lblUsername.setObjectName("lblUsername") self.lblTestlinkApiKey = QtWidgets.QLabel(self.tabSettings) self.lblTestlinkApiKey.setGeometry(QtCore.QRect(310, 10, 111, 17)) self.lblTestlinkApiKey.setObjectName("lblTestlinkApiKey") self.leUsername = QtWidgets.QLineEdit(self.tabSettings) self.leUsername.setGeometry(QtCore.QRect(10, 30, 291, 25)) self.leUsername.setObjectName("leUsername") self.leTestlinkApiKey = QtWidgets.QLineEdit(self.tabSettings) self.leTestlinkApiKey.setGeometry(QtCore.QRect(310, 30, 341, 25)) self.leTestlinkApiKey.setObjectName("leTestlinkApiKey") # combo box projects self.cbProjects = QtWidgets.QComboBox(self.tabSettings) self.cbProjects.setGeometry(QtCore.QRect(10, 150, 291, 25)) self.cbProjects.setObjectName("cbProjects") self.cbProjects.currentTextChanged.connect(self.getTestlinkPlans) self.leTestlinkAddress = QtWidgets.QLineEdit(self.tabSettings) self.leTestlinkAddress.setGeometry(QtCore.QRect(10, 90, 641, 25)) self.leTestlinkAddress.setObjectName("leTestlinkAddress") self.lblTestlinkAddress = QtWidgets.QLabel(self.tabSettings) self.lblTestlinkAddress.setGeometry(QtCore.QRect(10, 70, 101, 17)) self.lblTestlinkAddress.setObjectName("lblTestlinkAddress") # connect push button self.pbConnect = QtWidgets.QPushButton(self.tabSettings) self.pbConnect.setGeometry(QtCore.QRect(80, 330, 80, 25)) self.pbConnect.setObjectName("pbConnect") self.pbConnect.clicked.connect(self.getTestlinkProjects) self.lblSettingsStatus = QtWidgets.QLabel(self.tabSettings) self.lblSettingsStatus.setGeometry(QtCore.QRect(180, 330, 171, 21)) self.lblSettingsStatus.setObjectName("lblSettingsStatus") self.lblTestProject = QtWidgets.QLabel(self.tabSettings) self.lblTestProject.setGeometry(QtCore.QRect(10, 120, 91, 31)) self.lblTestProject.setObjectName("lblTestProject") # Combobox plans self.cbPlans = QtWidgets.QComboBox(self.tabSettings) self.cbPlans.setGeometry(QtCore.QRect(310, 150, 341, 25)) self.cbPlans.setObjectName("cbPlans") self.cbPlans.currentTextChanged.connect(self.getTeslinkPlatforms) self.lblTestPlan = QtWidgets.QLabel(self.tabSettings) self.lblTestPlan.setGeometry(QtCore.QRect(310, 126, 91, 21)) self.lblTestPlan.setObjectName("lblTestPlan") #Combobox platforms self.cbPlatforms = QtWidgets.QComboBox(self.tabSettings) self.cbPlatforms.setGeometry(QtCore.QRect(10, 210, 291, 25)) self.cbPlatforms.setObjectName("cbPlatforms") self.cbPlatforms.currentTextChanged.connect(self.getTestlinkBuilds) self.cbBuilds = QtWidgets.QComboBox(self.tabSettings) self.cbBuilds.setGeometry(QtCore.QRect(310, 210, 341, 25)) self.cbBuilds.setObjectName("cbBuilds") self.lblPlatform = QtWidgets.QLabel(self.tabSettings) self.lblPlatform.setGeometry(QtCore.QRect(10, 186, 91, 21)) self.lblPlatform.setObjectName("lblPlatform") self.lblBuild = QtWidgets.QLabel(self.tabSettings) self.lblBuild.setGeometry(QtCore.QRect(310, 186, 54, 21)) self.lblBuild.setObjectName("lblBuild") self.tabWidget.addTab(self.tabSettings, "") MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 676, 22)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(MainWindow) # RPTool init self.rpt = RPTool() self.setting = Settings('rptool.cfg') self.config = self.rpt.getConfig() self.interval = 0 self.setFolderDir() self.listTests() self.testlinkSettings() def saveSettings(self): value = self.cbBuilds.currentText() self.config['build_id'] = value.split('-')[0] self.config['build_name'] = value.split('-')[1] self.config['username'] = self.leUsername.text() print('config ', self.config) self.rpt.saveConfig(self.config) def testlinkSettings(self): self.leUsername.setText(self.rpt.getUsername()) self.leTestlinkAddress.setText(self.rpt.getUrl()) self.leTestlinkApiKey.setText(self.rpt.getApiKey()) def getTestlinkBuilds(self, value): print('loading build for', value) self.config['platform_id'] = value.split('-')[0] builds = self.rpt.getBuilds(self.config['plan_id']) self.cbBuilds.clear() for build in builds: id = build['id'] name = build['name'] self.cbBuilds.addItem('{0}-{1}'.format(id, name)) def getTeslinkPlatforms(self, value): print('loading platforms for', value) self.config['plan_id'] = value.split('-')[0] platforms = self.rpt.getPlatforms(self.config['project_id']) self.cbPlatforms.clear() for platform in platforms: id = platform['id'] name = platform['name'] self.cbPlatforms.addItem('{0}-{1}'.format(id, name)) def getTestlinkPlans(self, value): print('loading plans for ', value) self.config['project_id'] = int(value.split('-')[0]) plans = self.rpt.getPlans(self.config['project_id']) self.cbPlans.clear() for plan in plans: id = plan['id'] name = plan['name'] self.cbPlans.addItem('{0}-{1}'.format(id, name)) def getTestlinkProjects(self): print('load projects') projects = self.rpt.getProjects() self.cbProjects.clear() for project in projects: id = project['id'] prefix = project['prefix'] name = project['name'] self.cbProjects.addItem('{0}-{1}-{2}'.format(id, prefix, name)) def changeLoopState(self): if self.chboxLoop.isChecked(): self.interval = int(self.leInterval.text()) self.rpt.notifyLoop(self.interval) else: self.interval = 0 self.setting.saveConfig({'loop': self.interval}) cfg = self.setting.readConfig() def setFolderDir(self): self.leTestFolder.setText(self.rpt.getTestFolder()) def listTests(self): for test in self.rpt.getTestFiles(): self.lvTestCases.addItem(test) def addTestCase(self): tc_name = self.leTestCaseName.text() self.leTestCaseName.setText('') self.lvTestCases.addItem(tc_name) self.rpt.rec(tc_name) def itemSelected(self, item): print('selected ', item.text()) self.tcItem = item self.pbRec.setEnabled(False) self.pbPlay.setEnabled(True) self.pbDelete.setEnabled(True) def deleteTestCase(self): print('deleting ', self.tcItem.text()) self.lvTestCases.takeItem(self.lvTestCases.currentRow()) self.rpt.delete(self.tcItem.text()) self.pbRec.setEnabled(True) self.pbPlay.setEnabled(False) def playTestCase(self): print('playing ', self.tcItem.text()) self.pbRec.setEnabled(True) self.pbPlay.setEnabled(False) self.rpt.play(self.tcItem.text()) self.checkLoopEnabled() def checkLoopEnabled(self): cfg = self.setting.readConfig() if cfg['loop'] == '0': self.chboxLoop.setChecked(False) self.leInterval.setText('0') else: self.rpt.notifyLoop(cfg['loop']) time.sleep(int(cfg['loop'])) self.playTestCase() def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "RPTool Viewer")) self.pbRec.setText(_translate("MainWindow", "Rec")) self.lblTestCaseName.setText(_translate("MainWindow", "Test Case Name")) self.lblTestFolder.setText(_translate("MainWindow", "Test Folder")) self.pbPlay.setText(_translate("MainWindow", "Play")) self.pbDelete.setText(_translate("MainWindow", "Del")) self.pbPlayAll.setText(_translate("MainWindow", "Play All")) self.lblToolStatus.setText(_translate("MainWindow", "Not started yet!")) self.chboxLoop.setText(_translate("MainWindow", "Loop")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabRPTool), _translate("MainWindow", "RPTool")) self.pbSaveSettings.setText(_translate("MainWindow", "Save")) self.lblUsername.setText(_translate("MainWindow", "Username")) self.lblTestlinkApiKey.setText( _translate("MainWindow", "Testlink API KEY")) self.lblTestlinkAddress.setText( _translate("MainWindow", "Testlink Address")) self.pbConnect.setText(_translate("MainWindow", "Connect")) self.lblSettingsStatus.setText( _translate("MainWindow", "Not connected")) self.lblTestProject.setText(_translate("MainWindow", "Test Projects")) self.lblTestPlan.setText(_translate("MainWindow", "Test Plans")) self.lblPlatform.setText(_translate("MainWindow", "Platforms")) self.lblBuild.setText(_translate("MainWindow", "Builds")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabSettings), _translate("MainWindow", "Settings"))
class RPTool: def __init__(self): self.playback = None self.recorder = None self.log = Logger('RPTool') self.fileManager = FileManager() self.file = '' self.alert = Alert('RPTool') self.api = TestlinkAPI() self.setting = Settings('testlink.cfg') self.testFolder = 'artifacts' def getTestFolder(self): return '{0}/{1}'.format(self.fileManager.getCurrentFolder(), self.testFolder) def getTestFiles(self): return self.fileManager.list(self.getTestFolder()) def rec(self, test_name): self.alert.notify('Recording {} ...'.format(test_name)) self.recorder = Recorder(self.fileManager) self.log.debug('recording \'{0}\'...'.format(test_name)) self.saveTestFile(test_name) self.recorder.start(self.file) def delete(self, test_name): self.log.debug('deleting \'{0}\'...'.format(test_name)) self.fileManager.delete(self.getFullFileName(test_name)) self.alert.notify('Deleting {} ...'.format(test_name)) def play(self, test_name): self.alert.notify('Playback {0}'.format(test_name)) self.playback = PlayBack(self.fileManager) self.log.debug('playback \'{0}\'...'.format(test_name)) self.playback.play(self.getFullFileName(test_name)) def saveTestFile(self, test_name): self.file = self.getFullFileName(test_name) self.fileManager.write(self.file, '') self.log.debug('saving test file {0}'.format(self.file)) def getFullFileName(self, test_name): return '{0}/{1}'.format(self.getTestFolder(), test_name) def notifyLoop(self, time): self.alert.notify('Loop enabled to repeat in {} s'.format(time)) def getUsername(self): return self.api.getUsername() def getApiKey(self): return self.api.getApiKey() def getUrl(self): return self.api.getUrl() def getProjects(self): return self.api.getProjects() def getPlans(self, project_id): return self.api.getPlans(project_id) def getPlatforms(self, project_id): platforms = [] map = self.api.getPlatforms(project_id) for key in map: platforms.append(map[key]) return platforms def getBuilds(self, plan_id): return self.api.getBuilds(plan_id) def saveConfig(self, config): print('saving config', config) self.setting.saveConfig(config) def getConfig(self): cfg = self.setting.readConfig() if cfg is None: return {} else: return cfg