From e89a53b6e1183f779dd71adc2ed2370949ebdc01 Mon Sep 17 00:00:00 2001 From: Lan Hui Date: Sat, 23 Oct 2021 22:21:33 +0800 Subject: [PATCH] Selenium test scripts (written in Python) by a former undergraduate student IBRAHIM MOHAMED IBRAHIM ISMAIL at zjnu. --- .../test_kit/DUMMY_SUBMISSION.txt | 2 + test/SeleniumIbrahim/test_kit/Error_log.txt | 318 ++++++++++++++ test/SeleniumIbrahim/test_kit/README.md | 75 ++++ test/SeleniumIbrahim/test_kit/Test_Users.txt | 10 + test/SeleniumIbrahim/test_kit/actor.py | 16 + test/SeleniumIbrahim/test_kit/admin.py | 214 ++++++++++ .../SeleniumIbrahim/test_kit/assets/style.css | 186 +++++++++ test/SeleniumIbrahim/test_kit/course_code.txt | 1 + .../example_test_report_09032021_1927.html | 387 ++++++++++++++++++ test/SeleniumIbrahim/test_kit/instructor.py | 238 +++++++++++ test/SeleniumIbrahim/test_kit/student.py | 199 +++++++++ test/SeleniumIbrahim/test_kit/student_ids.txt | 12 + test/SeleniumIbrahim/test_kit/test_suite.py | 98 +++++ test/SeleniumIbrahim/test_kit/utility.py | 304 ++++++++++++++ 14 files changed, 2060 insertions(+) create mode 100644 test/SeleniumIbrahim/test_kit/DUMMY_SUBMISSION.txt create mode 100644 test/SeleniumIbrahim/test_kit/Error_log.txt create mode 100644 test/SeleniumIbrahim/test_kit/README.md create mode 100644 test/SeleniumIbrahim/test_kit/Test_Users.txt create mode 100644 test/SeleniumIbrahim/test_kit/actor.py create mode 100644 test/SeleniumIbrahim/test_kit/admin.py create mode 100644 test/SeleniumIbrahim/test_kit/assets/style.css create mode 100644 test/SeleniumIbrahim/test_kit/course_code.txt create mode 100644 test/SeleniumIbrahim/test_kit/example_test_report_09032021_1927.html create mode 100644 test/SeleniumIbrahim/test_kit/instructor.py create mode 100644 test/SeleniumIbrahim/test_kit/student.py create mode 100644 test/SeleniumIbrahim/test_kit/student_ids.txt create mode 100644 test/SeleniumIbrahim/test_kit/test_suite.py create mode 100644 test/SeleniumIbrahim/test_kit/utility.py diff --git a/test/SeleniumIbrahim/test_kit/DUMMY_SUBMISSION.txt b/test/SeleniumIbrahim/test_kit/DUMMY_SUBMISSION.txt new file mode 100644 index 0000000..d8cfb2c --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/DUMMY_SUBMISSION.txt @@ -0,0 +1,2 @@ +THIS IS A DUMMY SUBMISSION FILE FOR TESTING PURPOSES. +MAKE SURE TO KEEP THIS FILE IN THE SAME DIRECTORY AS THE TESTING SCRIPTS. \ No newline at end of file diff --git a/test/SeleniumIbrahim/test_kit/Error_log.txt b/test/SeleniumIbrahim/test_kit/Error_log.txt new file mode 100644 index 0000000..d151ed7 --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/Error_log.txt @@ -0,0 +1,318 @@ +[ERROR]14/04/2021-00:41:55>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 38, in create_new_account + new_account_form = wait2.until(presence_of_element_located(By.ID, "frm_create_acc")) +NameError: name 'presence_of_element_located' is not defined + +[ERROR]14/04/2021-00:42:58>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 38, in create_new_account + new_account_form = wait2.until(EC.presence_of_element_located(By.ID, "frm_create_acc")) +TypeError: __init__() takes 2 positional arguments but 3 were given + +[ERROR]14/04/2021-00:44:31>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 41, in create_new_account + name_field = new_account_form.find_element(By.XPATH, "//form[@id='frm_create_acc']\ + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 658, in find_element + return self._execute(Command.FIND_CHILD_ELEMENT, + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute + return self._parent.execute(command, params) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.InvalidSelectorException: Message: invalid selector: Unable to locate an element with the xpath expression //form[@id='frm_create_acc'] /input[@name='name' because of the following error: +SyntaxError: Failed to execute 'evaluate' on 'Document': The string '//form[@id='frm_create_acc'] /input[@name='name'' is not a valid XPath expression. + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-00:45:59>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 41, in create_new_account + name_field = new_account_form.find_element(By.XPATH, "//form[@id='frm_create_acc']/input[@name='name'") + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 658, in find_element + return self._execute(Command.FIND_CHILD_ELEMENT, + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute + return self._parent.execute(command, params) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.InvalidSelectorException: Message: invalid selector: Unable to locate an element with the xpath expression //form[@id='frm_create_acc']/input[@name='name' because of the following error: +SyntaxError: Failed to execute 'evaluate' on 'Document': The string '//form[@id='frm_create_acc']/input[@name='name'' is not a valid XPath expression. + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-00:47:04>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 41, in create_new_account + name_field = new_account_form.find_element(By.XPATH, "//form[@id='frm_create_acc']\ + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 658, in find_element + return self._execute(Command.FIND_CHILD_ELEMENT, + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute + return self._parent.execute(command, params) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//form[@id='frm_create_acc'] /input[@name='name']"} + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-00:47:44>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 41, in create_new_account + name_field = new_account_form.find_element(By.XPATH, "//form[@id='frm_create_acc']/input[@name='name']") + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 658, in find_element + return self._execute(Command.FIND_CHILD_ELEMENT, + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute + return self._parent.execute(command, params) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//form[@id='frm_create_acc']/input[@name='name']"} + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-00:49:28>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 41, in create_new_account + name_field = new_account_form.find_element(By.XPATH, "//form[@id='frm_create_acc']/input[@name='Full Name']") + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 658, in find_element + return self._execute(Command.FIND_CHILD_ELEMENT, + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute + return self._parent.execute(command, params) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//form[@id='frm_create_acc']/input[@name='Full Name']"} + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-00:51:31>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 41, in create_new_account + name_field = new_account_form.find_element(By.XPATH, "//input[@name='Full Name']") + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 658, in find_element + return self._execute(Command.FIND_CHILD_ELEMENT, + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute + return self._parent.execute(command, params) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//input[@name='Full Name']"} + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-13:56:18>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 139, in account_block_activate + exist_acc_tab = wait2.until(EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div[1]/div/ul/li[3]/a"))) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\support\wait.py", line 80, in until + raise TimeoutException(message, screen, stacktrace) +selenium.common.exceptions.TimeoutException: Message: + + +[ERROR]14/04/2021-13:57:14>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 150, in account_block_activate + block_btn.click() + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 80, in click + self._execute(Command.CLICK_ELEMENT) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute + return self._parent.execute(command, params) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted: Element is not clickable at point (582, 469). Other element would receive the click: + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-13:58:01>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 150, in account_block_activate + block_btn.click() + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 80, in click + self._execute(Command.CLICK_ELEMENT) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute + return self._parent.execute(command, params) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted: Element is not clickable at point (574, 469). Other element would receive the click: + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-14:02:29>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 149, in account_block_activate + block_btn = wait3.until(EC.presence_of_element_located((By.LINK_TEXT, "Block"))) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\support\wait.py", line 80, in until + raise TimeoutException(message, screen, stacktrace) +selenium.common.exceptions.TimeoutException: Message: + + +[ERROR]14/04/2021-14:15:58>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 144, in account_block_activate + exist_acc_tab = wait2.until(EC.presence_of_element_located((By.ID, "acc_table"))) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\support\wait.py", line 80, in until + raise TimeoutException(message, screen, stacktrace) +selenium.common.exceptions.TimeoutException: Message: + + +[ERROR]14/04/2021-14:18:19>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 145, in account_block_activate + exist_acc_tab.click() + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 80, in click + self._execute(Command.CLICK_ELEMENT) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute + return self._parent.execute(command, params) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-14:19:23>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\utility.py", line 47, in getSession + driver.get(self.getPageURL()) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 333, in get + self.execute(Command.GET, {'url': url}) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.WebDriverException: Message: unknown error: cannot determine loading status +from disconnected: received Inspector.detached event + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-14:19:28>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\utility.py", line 47, in getSession + driver.get(self.getPageURL()) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 333, in get + self.execute(Command.GET, {'url': url}) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.WebDriverException: Message: unknown error: cannot determine loading status +from disconnected: received Inspector.detached event + (Session info: chrome=89.0.4389.114) + + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\utility.py", line 70, in login + driver = self.getSession() + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\utility.py", line 55, in getSession + self.killSession(driver) + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\utility.py", line 145, in killSession + driver.close() + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 688, in close + self.execute(Command.CLOSE) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.WebDriverException: Message: chrome not reachable + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-14:19:28>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\utility.py", line 47, in getSession + driver.get(self.getPageURL()) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 333, in get + self.execute(Command.GET, {'url': url}) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.WebDriverException: Message: unknown error: cannot determine loading status +from disconnected: received Inspector.detached event + (Session info: chrome=89.0.4389.114) + + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\utility.py", line 70, in login + driver = self.getSession() + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\utility.py", line 55, in getSession + self.killSession(driver) + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\utility.py", line 145, in killSession + driver.close() + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 688, in close + self.execute(Command.CLOSE) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.WebDriverException: Message: chrome not reachable + (Session info: chrome=89.0.4389.114) + + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 135, in account_block_activate + driver = self.utility.login(self) + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\utility.py", line 85, in login + self.killSession(driver) +UnboundLocalError: local variable 'driver' referenced before assignment + +[ERROR]14/04/2021-14:23:07>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 150, in account_block_activate + block_btn.click() + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 80, in click + self._execute(Command.CLICK_ELEMENT) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute + return self._parent.execute(command, params) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted: Element is not clickable at point (574, 469). Other element would receive the click: + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-14:32:06>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 149, in account_block_activate + block_btn = wait2.until(EC.presence_of_element_located((By.XPATH, "//table[@id='acc_table']/button[@id='block_acc_1']"))) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\support\wait.py", line 80, in until + raise TimeoutException(message, screen, stacktrace) +selenium.common.exceptions.TimeoutException: Message: + + +[ERROR]14/04/2021-14:34:01>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 150, in account_block_activate + block_btn.find_element(By.LINK_TEXT, "Block").click() + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 658, in find_element + return self._execute(Command.FIND_CHILD_ELEMENT, + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute + return self._parent.execute(command, params) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"link text","selector":"Block"} + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-14:35:58>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 151, in account_block_activate + block_btn.click() + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 80, in click + self._execute(Command.CLICK_ELEMENT) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute + return self._parent.execute(command, params) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute + self.error_handler.check_response(response) + File "C:\Users\Ibrahim\AppData\Local\Programs\Python\Python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response + raise exception_class(message, screen, stacktrace) +selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted: Element is not clickable at point (574, 469). Other element would receive the click: + (Session info: chrome=89.0.4389.114) + + +[ERROR]14/04/2021-16:41:19>Traceback (most recent call last): + File "D:\Graduation Thesis\LRR workstation\Testing Workstation\test_github\LRR\test_kit\admin.py", line 206, in assign_TA + assign_btn,click() +NameError: name 'click' is not defined + diff --git a/test/SeleniumIbrahim/test_kit/README.md b/test/SeleniumIbrahim/test_kit/README.md new file mode 100644 index 0000000..9547109 --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/README.md @@ -0,0 +1,75 @@ +# Important notes +Before executing this test suite, there are several things need to be setup and taken into considaration. + +# Setup test environment +These test scripts are written using Python, uses selenium webdriver for automation, and pytest for test execution and reporting. Hence, you have to install +Python on your machine, then pip install selenium webdriver and pytest-selenium libraries. + +Here we guide you through the installation of selenium and pytest-selenium only assuming that you already have Python environment on your machine. If not, visit +[the official Python page](https://www.python.org/downloads/) to download and install Python. + +## Selenium webdriver +Since Selenium supports many web browsers and each browser has its own webdriver, first you will need to download +[Google chrome webdriver](https://sites.google.com/a/chromium.org/chromedriver/downloads). +> **_NOTE:_** Make sure to download the suitable webdriver to your version of Google chrome browser + +Then, add the webdriver to your system environment variables so it would be accessible by the scripts without the need to excplicitly attach the webdriver to the test kit directory. +To achieve that: +1- left click on **This PC**, then choose **properties**. +2- choose **Advanced system settings**. +3- choose **Environment variables**. +4- under **System variables** double-click on **path**. +5- choose **New**, copy and paste path to the downloaded webdriver executable. +> **_NOTE:_** To avoid problems with long path strings, it is recommended that you create a folder in your `C:\` drive such as: +> `C:\webdriver\bin` and append the webdriver executable into it. + +To test if everything is working fine, open a command prompt and issue the following command: +>`C:\> chromedriver` + +You should get something like the following: + +>`Starting ChromeDriver 88.0.4324.96 (68dba2d8a0b149a1d3afac56fa74648032bcf46b-refs/branch-heads/4324@{#1784}) on port 9515` +>`Only local connections are allowed.` +>`Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.` +>`ChromeDriver was started successfully.` + +After that, pip install selenium library using the following command on your command prompt: + +>`C:\> pip install selenium` + +That is all for selenium, next we guide you through installing pytest-selenium. + +## Pytest-selenium +Simply, on a command prompt issue the following command: +>`C:\ pip install pytest-selenium` + +For more details about pytest-selenium visit [this page](https://pytest-selenium.readthedocs.io/en/latest/installing.html). + +# Text files descriptions + +As you have noticed already, there are several `.txt` files in the test kit folder, namely: +* course_code.txt +* DUMMY_SUBMISSION.txt +* Error_log.txt +* student_ids.txt +* Test_Users.txt + +Each file is essential to the automation operation in some sense, for example `course_code.txt` works as a short-term memory to store course codes created by the automation +scripts in some test cases that needed to be used later in a subsequent test cases that requires the same course code to successfully run the test case. And `DUMMY_SUBMISSION.txt` that is used as a dummy file to be submitted during execution of lab report submission test case. +`Error_log.txt` is used for debugging and keeping track of problems encountered during test execution. +`student_ids.txt` is a static memory to store several student IDs pre-inserted into `student_data` table of `lrr` database and used by signup test case automation. +`Test_Users.txt` not used by the scripts, but contains two main system actors, namely, instructor and student accounts credentials that is provided manually inside the test scripts to carry out different operations for different system users. + +# Running the test suite + +> **_NOTE:_** Before running the test suite make sure that you have followed [these instructions](https://github.com/hema-001/LRR#installation-instructions) on how to install and run LRR on your machine. + +On the same local directory of your branch of LRR, and after you have done your contribution to the source code whether it is a bug fix or a feature integration and before committing your changes, you should run this regression test suite to prevent the addition of new bugs to the source code, and to ensure master repo consistency. + +Open a command prompt and issue the following command: +>`C:\ pytest --html test_report_xxxx_yyyy.html` + +The option `--html` tells pytest to generate an HTML test report with test execution results, which is useful and later must be provided before merging new pull requests to master repo. +`test_report_xxxx_yyyy.html` is the name of the HTML file to be generated after the completion of test execution. Where `xxxx` stands for the date string, for example `10032021` translates to March 3rd 2021, and `yyyy` stands for now time of test execution, for example `1955` translates to 19:55. +After running the command and if everything is setup correctly, you should see series of web automations for different main functions of LRR. +Finally, an HTML test report file will be generated just like the example test report in the test kit folder `example_test_report_09032021_1927.html`. diff --git a/test/SeleniumIbrahim/test_kit/Test_Users.txt b/test/SeleniumIbrahim/test_kit/Test_Users.txt new file mode 100644 index 0000000..bcd2a0c --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/Test_Users.txt @@ -0,0 +1,10 @@ +The following two users must be created before executing the test suite since they represent +two generic main actors of LRR, an instructor and a student. + +Instructor User: +Uname = 202032070221 +Pass = aA124536! + +Student User: +Uname = 202032070222 +Pass = aA124536! diff --git a/test/SeleniumIbrahim/test_kit/actor.py b/test/SeleniumIbrahim/test_kit/actor.py new file mode 100644 index 0000000..bbf00a2 --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/actor.py @@ -0,0 +1,16 @@ +class Actor: + def __init__(self, password, student_id): + self.password = password + self.student_id = student_id + + def setPassword(self, password): + self.password = password + + def setStudentID(self, student_id): + self.student_id = student_id + + def getPassword(self): + return self.password + + def getStudentID(self): + return self.student_id \ No newline at end of file diff --git a/test/SeleniumIbrahim/test_kit/admin.py b/test/SeleniumIbrahim/test_kit/admin.py new file mode 100644 index 0000000..4d6ede2 --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/admin.py @@ -0,0 +1,214 @@ +from actor import Actor +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +import sys, traceback + +class Admin(Actor): + def __init__(self, password, student_id, utility): + super().__init__(password, student_id) + self.utility = utility + + + def create_new_account(self, acc_id, type="TA"): + """This method automates and insturctor creating new account from + "Admin" page in LRR. + + Parameters: + - type: string + instructor: creates a new instructor account. + TA: creates a new teaching assistant account, This is the default. + - acc_id: string + account ID. + + Returns: + - 0 on success. + - 1 on failure to complete case execution. + """ + + try: + #Initiate driver session and login + driver = self.utility.login(self) + + #Locate "Admin" tab. + wait = WebDriverWait(driver, 10) + admin = wait.until(EC.presence_of_element_located((By.XPATH, "/html/body/nav/div/form/a[1]"))) + admin.click() + + #Locate "Create Lecturer/TA account" form + wait2 = WebDriverWait(driver, 10) + new_account_form = wait2.until(EC.presence_of_element_located((By.ID, "frm_create_acc"))) + + #Fill in the form fields + name_field = new_account_form.find_element(By.XPATH, "//input[@name='fullname']") + f_name = self.utility.random_string(4) + l_name = self.utility.random_string(5) + name_field.send_keys(f_name+" "+l_name) + + email_field = new_account_form.find_element(By.XPATH, "//input[@name='email']") + email_field.send_keys(f_name+"."+l_name+"@testing.com") + + id_field = new_account_form.find_element(By.XPATH, "//input[@name='passport']") + id_field.send_keys(acc_id) + + #If "Lecturer" is specified in type parameter, select Lecturer account type, else use default + if type == "Lecturer": + type_radio = new_account_form.find_element(By.XPATH, "//input[@value='Lecturer']") + else: + type_radio = new_account_form.find_element(By.XPATH, "//input[@value='TA']") + + type_radio.click() + + submit_btn = new_account_form.find_element(By.XPATH, "//input[@type='submit'][@value='Create']") + submit_btn.click() + + return 0 + + except: + print("There was a problem executing this test case") + print("Error in \"create_new_account()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.utility.log_error(err_msg) + print("Treminating session") + self.utility.killSession(driver) + return 1 + + def batch_create_acc(self, lst): + """This method automates an instructor batch creating new student account + under "Admin" tab. + + Paramaeters: + - lst: list + A list of student number strings to be batch created. + + Return: + - 0 on success + - 1 on failure to complete case execution. + """ + + try: + #Initiate and login + driver = self.utility.login(self) + + #Locate the "Admin" tab. + wait = WebDriverWait(driver, 10) + admin = wait.until(EC.presence_of_element_located((By.XPATH, "/html/body/nav/div/form/a[1]"))) + admin.click() + + #Navigate to "Batch create form" + wait2 = WebDriverWait(driver, 10) + batch_tab = wait2.until(EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div[1]/div/ul/li[2]/a"))) + batch_tab.click() + + #Fill in the form + wait3 = WebDriverWait(driver, 10) + batch_form = wait3.until(EC.presence_of_element_located((By.ID, "frm_batch_acc"))) + + text_area = batch_form.find_element(By.XPATH, "//textarea[@name='users']") + for i in range(len(lst)): + text_area.send_keys(lst[i]+" ") + + submit_btn = batch_form.find_element(By.XPATH, "//input[@type='submit'][@value='Create All']") + submit_btn.click() + + except: + print("There was a problem executing this test case") + print("Error in \"batch_create_acc()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.utility.log_error(err_msg) + print("Treminating session") + self.utility.killSession(driver) + return 1 + + def account_block_activate(self, op): + """This method automates an instructor blocking/activating an existing account + under "Admin" page. + + Parameters: + op: string + - block: to block first account on existing account table + - activate: to activate first account on existing account table + + Returns: + - 0 on success + - 1 on failure to complete case execution. + """ + try: + #Initiate web driver session and login + driver = self.utility.login(self) + + #Locate the "Admin" tab. + wait = WebDriverWait(driver, 10) + admin = wait.until(EC.presence_of_element_located((By.XPATH, "/html/body/nav/div/form/a[1]"))) + admin.click() + + #Navigate to "Exisitin Accounts" table + wait2 = WebDriverWait(driver, 10) + exist_acc_tab = wait2.until(EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div[1]/div/ul/li[3]/a"))) + exist_acc_tab.click() + + #Alternate between block and activate + if op == "block": + #Locate first account and block it on existing account table + wait3 = WebDriverWait(driver, 10) + button = driver.find_element_by_id("block_acc_1") + elif op == "activate": + wait3 = WebDriverWait(driver, 10) + button = driver.find_element_by_id("activate_acc_1") + + driver.execute_script("arguments[0].click();", button) + + #Confirm pop-up alert + wait4 = WebDriverWait(driver, 10) + alert = wait4.until(EC.alert_is_present()) + alert.accept() + + return 0 + + except: + print("There was a problem executing this test case") + print("Error in \"account_block_activate()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.utility.log_error(err_msg) + print("Treminating session") + self.utility.killSession(driver) + return 1 + + def assign_TA(self): + """This method automates an instructor assigning a TA to a course + under "Admin" page. + + Returns: + - 0 on success + - 1 on failure to complete case execution. + """ + try: + #Initiate web driver session and login + driver = self.utility.login(self) + + #Locate the "Admin" tab. + wait = WebDriverWait(driver, 10) + admin = wait.until(EC.presence_of_element_located((By.XPATH, "/html/body/nav/div/form/a[1]"))) + admin.click() + + #Navigate to "Existing Courses" table. + wait2 = WebDriverWait(driver, 10) + courses_table = wait2.until(EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div[2]/div/ul/li[2]/a"))) + courses_table.click() + + #Assign TA to the first course in "Existing Courses" table + wait3 = WebDriverWait(driver, 10) + drop_menu_form = wait3.until(EC.presence_of_element_located((By.XPATH, "//*[@id='menub']/table/tbody/tr[2]/td[5]/form"))) + assign_btn = drop_menu_form.find_element(By.XPATH, "//input[@type='submit'][@value='assign']") + assign_btn.click() + + return 0 + + except: + print("There was a problem executing this test case") + print("Error in \"assign_TA()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.utility.log_error(err_msg) + print("Treminating session") + self.utility.killSession(driver) + return 1 \ No newline at end of file diff --git a/test/SeleniumIbrahim/test_kit/assets/style.css b/test/SeleniumIbrahim/test_kit/assets/style.css new file mode 100644 index 0000000..3edac88 --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/assets/style.css @@ -0,0 +1,186 @@ +body { + font-family: Helvetica, Arial, sans-serif; + font-size: 12px; + /* do not increase min-width as some may use split screens */ + min-width: 800px; + color: #999; +} + +h1 { + font-size: 24px; + color: black; +} + +h2 { + font-size: 16px; + color: black; +} + +p { + color: black; +} + +a { + color: #999; +} + +table { + border-collapse: collapse; +} + +/****************************** + * SUMMARY INFORMATION + ******************************/ +#environment td { + padding: 5px; + border: 1px solid #E6E6E6; +} +#environment tr:nth-child(odd) { + background-color: #f6f6f6; +} + +/****************************** + * TEST RESULT COLORS + ******************************/ +span.passed, +.passed .col-result { + color: green; +} + +span.skipped, +span.xfailed, +span.rerun, +.skipped .col-result, +.xfailed .col-result, +.rerun .col-result { + color: orange; +} + +span.error, +span.failed, +span.xpassed, +.error .col-result, +.failed .col-result, +.xpassed .col-result { + color: red; +} + +/****************************** + * RESULTS TABLE + * + * 1. Table Layout + * 2. Extra + * 3. Sorting items + * + ******************************/ +/*------------------ + * 1. Table Layout + *------------------*/ +#results-table { + border: 1px solid #e6e6e6; + color: #999; + font-size: 12px; + width: 100%; +} +#results-table th, +#results-table td { + padding: 5px; + border: 1px solid #E6E6E6; + text-align: left; +} +#results-table th { + font-weight: bold; +} + +/*------------------ + * 2. Extra + *------------------*/ +.log { + background-color: #e6e6e6; + border: 1px solid #e6e6e6; + color: black; + display: block; + font-family: "Courier New", Courier, monospace; + height: 230px; + overflow-y: scroll; + padding: 5px; + white-space: pre-wrap; +} +.log:only-child { + height: inherit; +} + +div.image { + border: 1px solid #e6e6e6; + float: right; + height: 240px; + margin-left: 5px; + overflow: hidden; + width: 320px; +} +div.image img { + width: 320px; +} + +div.video { + border: 1px solid #e6e6e6; + float: right; + height: 240px; + margin-left: 5px; + overflow: hidden; + width: 320px; +} +div.video video { + overflow: hidden; + width: 320px; + height: 240px; +} + +.collapsed { + display: none; +} + +.expander::after { + content: " (show details)"; + color: #BBB; + font-style: italic; + cursor: pointer; +} + +.collapser::after { + content: " (hide details)"; + color: #BBB; + font-style: italic; + cursor: pointer; +} + +/*------------------ + * 3. Sorting items + *------------------*/ +.sortable { + cursor: pointer; +} + +.sort-icon { + font-size: 0px; + float: left; + margin-right: 5px; + margin-top: 5px; + /*triangle*/ + width: 0; + height: 0; + border-left: 8px solid transparent; + border-right: 8px solid transparent; +} +.inactive .sort-icon { + /*finish triangle*/ + border-top: 8px solid #E6E6E6; +} +.asc.active .sort-icon { + /*finish triangle*/ + border-bottom: 8px solid #999; +} +.desc.active .sort-icon { + /*finish triangle*/ + border-top: 8px solid #999; +} diff --git a/test/SeleniumIbrahim/test_kit/course_code.txt b/test/SeleniumIbrahim/test_kit/course_code.txt new file mode 100644 index 0000000..78ca105 --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/course_code.txt @@ -0,0 +1 @@ +TC14042021171102 \ No newline at end of file diff --git a/test/SeleniumIbrahim/test_kit/example_test_report_09032021_1927.html b/test/SeleniumIbrahim/test_kit/example_test_report_09032021_1927.html new file mode 100644 index 0000000..4287712 --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/example_test_report_09032021_1927.html @@ -0,0 +1,387 @@ + + + + + Test Report + + + +

test_report09032021_1927.html

+

Report generated on 09-Mar-2021 at 19:32:47 by pytest-html v3.1.1

+

Environment

+ + + + + + + + + + + + + + + + + + + + + + + + +
Base URL
Capabilities{}
DriverNone
JAVA_HOMEC:\Program Files\Java\jdk-15.0.1
Packages{"pluggy": "0.13.1", "py": "1.10.0", "pytest": "6.2.2"}
PlatformWindows-10-10.0.19041-SP0
Plugins{"base-url": "1.4.2", "html": "3.1.1", "metadata": "1.11.0", "selenium": "2.0.1", "variables": "1.9.0"}
Python3.9.1
+

Summary

+

11 tests ran in 294.71 seconds.

+ 11 passed, 0 skipped, 0 failed, 0 errors, 0 expected failures, 0 unexpected passes +

Results

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ResultTestDurationLinks
Passedtest_suite.py::test_case_0016.03
+
No log output captured.
Passedtest_suite.py::test_case_0122.51
+
No log output captured.
Passedtest_suite.py::test_case_0238.36
+
No log output captured.
Passedtest_suite.py::test_case_0324.65
+
No log output captured.
Passedtest_suite.py::test_case_0425.31
+
No log output captured.
Passedtest_suite.py::test_case_0533.63
+
No log output captured.
Passedtest_suite.py::test_case_0624.20
+
No log output captured.
Passedtest_suite.py::test_case_0726.00
+
No log output captured.
Passedtest_suite.py::test_case_0823.13
+
No log output captured.
Passedtest_suite.py::test_case_0926.61
+
No log output captured.
Passedtest_suite.py::test_case_1033.16
+
------------------------------Captured stdout call------------------------------
There was a problem executing this test case +Error in "create_course_group()" method, see error_log.txt for more details +Treminating session +
\ No newline at end of file diff --git a/test/SeleniumIbrahim/test_kit/instructor.py b/test/SeleniumIbrahim/test_kit/instructor.py new file mode 100644 index 0000000..a738c2d --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/instructor.py @@ -0,0 +1,238 @@ +from actor import Actor +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +import sys, traceback + +class Instructor(Actor): + + """ + This class contains all the major scenarios an instructor system actor can execute + automated by Selenium framework. + + Attributes: + - password: password string. + - student_id: student ID number (or Instructor ID). + - utility: a utility object of the MyUtility class. + + Methods: + - private login(): automates login process. + - post_lab_report(): automates instructor posting lab report submission portal for students + to submit assignments. + - create_course_portal(): automates instructor creating course portal for students + to join a course. + - post_lab_report_results(): automates instructor announcing lab submission marking + decission. + - manage_deadline(): automates instructor extending lab report submission portal deadline. + + TODO: + - manage_deadline() + """ + + def __init__(self, password, student_id, utility): + super().__init__(password, student_id) + self.utility = utility + + def post_lab_report(self, group = 0): + + """This method automates instructor posting lab report submission portal for students + for students to submit assignment. + + Parameters: + - group: int + 0 indicates that this lab report submission portal is intended for individuals + 1 indicates that this lab report submission portal is intended for groups + + Returns: + - 0 on success. + - 1 on failure to complete case execution. + + """ + + try: + #Login + driver = self.utility.login(self) + + #Navigate to course page + self.utility.open_course_page(driver) + + dateStr = self.utility.getTodayDate() # this is needed for deadline. + + #wait until the lab submission form shows up + wait2 = WebDriverWait(driver, 10) + new_lab_assignment_form = wait2.until(EC.presence_of_element_located((By.XPATH, "//form[@id='nlaf']"))) + + #Fill the required form fields and submit. + lab_date = new_lab_assignment_form.find_element(By.XPATH, "//input[@id='date'][@name='deadlinedate']") + timeStr = self.utility.getTodayDate() + lab_date.send_keys(timeStr) + lab_title = new_lab_assignment_form.find_element(By.XPATH, "//form[@id='nlaf']/input[@id='ltitle']") + lab_title.send_keys("TESTASSIGNMENT"+str(dateStr)) + lab_instructions = new_lab_assignment_form.find_element(By.XPATH, "//form[@id='nlaf']/textarea[@id='linstruct']") + lab_instructions.send_keys("TESTINSTRUCTIONS"+str(dateStr)) + lab_marks = new_lab_assignment_form.find_element(By.XPATH, "//form[@id='nlaf']/input[@id='lmark']") + lab_marks.send_keys("4") + + #if group == 0 select individual submission, if group == 1 select group submission + if group == 0: + submission_type = new_lab_assignment_form.find_element(By.XPATH, "//form[@id='nlaf']/input[@id='lindi']") + elif group == 1: + submission_type = new_lab_assignment_form.find_element(By.XPATH, "//form[@id='nlaf']/input[@id='lgrp']") + submission_type.click() + submit = new_lab_assignment_form.find_element(By.XPATH, "//form[@id='nlaf']/input[@id='lbtn']") + submit.click() + return 0 + except: + print("There was a problem executing this test case") + print("Error in \"post_lab_report()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.utility.log_error(err_msg) + print("Treminating session") + self.utility.killSession(driver) + return 1 + + def create_course_portal(self, joining = 0): + + """This method automates instructor creating course portal for students + to join a course. + + Parameters: + - joining: int + 0 indicates that this course does not require approval to join + 1 indicates that this course require approval to join + + Returns: + - 0 on success. + - 1 on failure to complete case execution. + + """ + try: + #Login first in order to create new course. + driver = self.utility.login(self) + + #These needed to create a unique course name based on date and time. + dateStr = self.utility.getTodayDate() + timeStr = self.utility.getTime() + + #Fill the required form fields and submit + course_name = driver.find_element(By.ID, "cname") + course_name.send_keys("TESTCOURSE"+str(dateStr)+str(timeStr)) + + #This URL is needed to access the same created course via its link in post_lab_report() method. + global courseURL + courseURL = "TESTCOURSE"+str(dateStr)+str(timeStr) + course_code = driver.find_element(By.ID, "ccode") + course_code.send_keys("TC"+str(dateStr)+str(timeStr)) + self.utility.storeCourseCode("TC"+str(dateStr)+str(timeStr))#Store course code to be used + academic_year = driver.find_element(By.ID, "ayear") #later by student. + academic_year.send_keys("2021") + faculty = driver.find_element(By.ID, "fac") + faculty.send_keys("TESTING DEPARTMENT") + + #If joining == 0 does not require join approval, if joining == 1 requires join approval + if joining == 0 : + joining_students = driver.find_element(By.ID, "jno") + elif group == 1: + joining_students = driver.find_element(By.ID, "jyes") + joining_students.click() + submit = driver.find_element(By.ID, "portal_btn") + submit.click() + return 0 + + except: + print("There was a problem executing this test case") + print("Error in \"create_course_portal()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.utility.log_error(err_msg) + print("Treminating session...") + self.utility.killSession(driver) + return 1 + + def mark_submission(self): + + """This method automates an instructor marking a lab submission. + + Returns: + - 0 on success. + - 1 on failure to complete case execution. + """ + try: + #Login + driver = self.utility.login(self) + + #Navigate to course page + self.utility.open_course_page(driver) + + #Wait until the submission portal card appears + wait2 = WebDriverWait(driver, 10) + view = wait2.until(EC.presence_of_element_located((By.ID, "view_btn"))) + view.click() + + #Locate and click the 'Mark Submission' btn + wait3 = WebDriverWait(driver, 10) + mark_submission = wait3.until(EC.presence_of_element_located((By.ID, "mark_btn"))) + mark_submission.click() + + #Fill and submit marking descision + wait4 = WebDriverWait(driver, 10) + pop_up = wait4.until(EC.presence_of_element_located((By.ID, "submit-form"))) + mark = pop_up.find_element(By.ID, "marks") + mark.send_keys("4") + comment = pop_up.find_element(By.ID, "feedback") + comment.send_keys("TESTCOMMENT") + submit = pop_up.find_element(By.XPATH, "/html/body/div[4]/div[2]/div/button[1]") + submit.click() + return 0 + + except: + print("There was a problem executing this test case") + print("Error in \"mark_submission()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.utility.log_error(err_msg) + print("Treminating session...") + self.utility.killSession(driver) + return 1 + + def manage_deadline(self): + + """This method automates instructor extending lab report submission portal deadline. + + Returns: + - 0 on success. + - 1 on failure to complete case execution. + + """ + try: + #Login + driver = self.utility.login(self) + + #Navigate to course page + self.utility.open_course_page(driver) + + #Wait until lab report assignment list appears. + wait = WebDriverWait(driver, 10) + extend_deadline = wait.until(EC.presence_of_element_located((By.ID, "ext_btn"))) + extend_deadline.click() + + #Wait until the extend deadline popup window shows up. + wait2 = WebDriverWait(driver, 10) + extend_deadline_form = wait2.until(EC.presence_of_element_located((By.ID, "frm"))) + + #Insert the new deadline and submit for all. + new_date = extend_deadline_form.find_element(By.XPATH, "//form[@id='frm']/input[3]") + dateStr = self.utility.getTomorrowDate() + new_date.send_keys(str(dateStr)) + target = extend_deadline_form.find_element(By.XPATH, "//form[@id='frm']/input[5]") + target.click() + submit = extend_deadline_form.find_element(By.XPATH, "/html/body/div[3]/div[2]/div/button[1]") + submit.click() + return 0 + + except: + print("There was a problem executing this test case") + print("Error in \"manage_deadline()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.utility.log_error(err_msg) + print("Treminating session...") + self.utility.killSession(driver) + return 1 \ No newline at end of file diff --git a/test/SeleniumIbrahim/test_kit/student.py b/test/SeleniumIbrahim/test_kit/student.py new file mode 100644 index 0000000..acbe814 --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/student.py @@ -0,0 +1,199 @@ +from actor import Actor +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +import os +import traceback + +class Student(Actor): + + """ + This class contains all the major scenarios a student system actor can execute + automated by Selenium framework. + + Attributes: + - password: password string. + - student_id: student ID number. + - utility: a utility object of the MyUtility class. + + Methods: + - signup(): automates student registration process. + - join_course(): automates student joining a course created by instructor. + - submit_assignment(): automates student assignment submission process. + - request_remarking(): automates student requesting lab report remarking request. + - create_course_group(): automates student creating course group. + - join_course_group(): automates student joining course group. + + TODO: + - join_course_group() + """ + + def __init__(self, password, student_id, utility): + super().__init__(password, student_id) + self.utility = utility + + def join_course(self): + + """ This metohd automates student joining a course created by instructor. + + Returns: + - driver: selenium.webdriver object, or + - 1 on failure to complete case execution. + + """ + try: + #Login in order to join a course. + driver = self.utility.login(self) + + #Search for course by its code. + wait = WebDriverWait(driver, 10) + course_code_field = wait.until(EC.presence_of_element_located((By.ID, "search_field"))) + course_code = self.utility.getCourseCode() + course_code_field.send_keys(course_code) + find_btn = driver.find_element(By.ID, "find_btn") + find_btn.click() + + #Wait until the course is found, and join. + wait2 = WebDriverWait(driver, 10) + course_card = wait.until(EC.element_to_be_clickable((By.LINK_TEXT, str("Join Course")))) + course_card.click() + + #If the joined course successfully, proceed. + wait3 = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "/html/body/div[1]/div[1]/span"))) + return 0 + + #Else, if exception happened, abort. + except: + print("There was a problem executing this test case") + print("Error in \"join_course()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.utility.log_error(err_msg) + print("Treminating session") + self.utility.killSession(driver) + return 1 + def submit_assignment(self): + + """ This method automates student assignment submission process. + + Returns: + - 0 on success. + - 1 on failure to complete case execution. + + """ + try: + #Join the course to submit assignmment. + driver = self.utility.login(self) + + #Navigate to course page. + self.utility.open_course_page(driver) + + #Locate and click assignment submission button. + wait2 = WebDriverWait(driver, 10) + assignment_card = wait2.until(EC.element_to_be_clickable((By.LINK_TEXT, "Submit Lab Report"))) + assignment_card.click() + + #Locate the assignment submission form and fill in the required data. + wait3 = WebDriverWait(driver, 10) + assignment_form = wait3.until(EC.presence_of_element_located((By.ID, "sub_form"))) + title = assignment_form.find_element(By.ID, "title") + dateStr = self.utility.getTodayDate() + timeStr = self.utility.getTime() + title.send_keys("TESTSUBMISSIOM"+dateStr+timeStr) + attachment = assignment_form.find_element(By.ID, "att_one") + attachment.send_keys(os.getcwd()+"/DUMMY_SUBMISSION.txt") + submit = driver.find_element(By.ID, "submit_btn") + submit.click() + return 0 + + except: + print("There was a problem executing this test case") + print("Error in \"submit_assignment()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.utility.log_error(err_msg) + print("Treminating session") + self.utility.killSession(driver) + return 1 + + def request_remarking(self): + + """ This method automates student creating course group. + + Returns: + - 0 on success. + - 1 on failure to complete case execution. + + """ + + try: + #Login in order to proceed to remarking request. + driver = self.utility.login(self) + + #Locate the course by its code and open the course page. + self.utility.open_course_page(driver) + + #Locate the remarking request button and click it. + wait2 = WebDriverWait(driver, 10) + marked_tab = wait2.until(EC.presence_of_element_located((By.ID, "marked_tab"))) + marked_tab.click() + req_remark = driver.find_element(By.ID, "req_remark") + req_remark.click() + + #Fill in the remarking form and submit. + wait3 = WebDriverWait(driver, 10) + alert = wait3.until(EC.alert_is_present()) + alert.send_keys("TESTREASON") + alert.accept() + return 0 + + except: + print("There was a problem executing this test case") + print("Error in \"request_remarking()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.utility.log_error(err_msg) + print("Treminating session") + self.utility.killSession(driver) + return 1 + + def create_course_group(self): + + """ This method automates student requesting lab report remarking request. + + Returns: + - 0 on success. + - 1 on failure to complete case execution. + + """ + try: + #Login in order to create course group. + driver = self.utility.login(self) + + #Locate the course in which the group to be created. + self.utility.open_course_page(driver) + + #Locate the create course group button and click it. + wait2 = WebDriverWait(driver, 10) + create_group = wait2.until(EC.presence_of_element_located((By.ID, "g_create_btn"))) + create_group.click() + + #Fill in the course group form and create. + wait3 = WebDriverWait(driver, 10) + group_form = wait3.until(EC.presence_of_element_located((By.ID, "frm"))) + timeStr = self.utility.getTime() + dateStr = self.utility.getTodayDate() + group_name = group_form.find_element(By.ID, "g_name") + group_name.send_keys("TESTGROUP"+str(dateStr)+str(timeStr)) + create = group_form.find_element(By.XPATH, "//div[2]/div/button[1]") + create.click() + return 0 + + except: + print("There was a problem executing this test case") + print("Error in \"create_course_group()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.utility.log_error(err_msg) + print("Treminating session") + self.utility.killSession(driver) + return 1 + + def join_course_group(self): + pass \ No newline at end of file diff --git a/test/SeleniumIbrahim/test_kit/student_ids.txt b/test/SeleniumIbrahim/test_kit/student_ids.txt new file mode 100644 index 0000000..75aad3a --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/student_ids.txt @@ -0,0 +1,12 @@ +#After you have inserted the student IDs into 'student_data' table of lrr, +#append the same student IDs here to be used for signup automation. +#Make sure that ALL student IDs here are ALREADY HAVE BEEN INSERTED into student_data table before executing +#the tests otherwise it will signup() text case will fail. +#The following students IDs are examples only, replace them according to the student IDs inserted +#into the database by you. Suppose we have inserted 202032020727, 202032020728, 202032020729 into +#the 'student_data' table of LRR database, then we would post them here as well (DO NOT START LINES WITH '#' +# OTHERWISE IT WILL BE IGNORED). +202032020726 +202032020727 +202032020728 +202032020729 \ No newline at end of file diff --git a/test/SeleniumIbrahim/test_kit/test_suite.py b/test/SeleniumIbrahim/test_kit/test_suite.py new file mode 100644 index 0000000..082d1b5 --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/test_suite.py @@ -0,0 +1,98 @@ +""" +LRR Testing automation scripts, written by Ibrahim M.I. Ismail in Feb 2021 as part +of the undergraduate thesis "Defect Analysis for LRR". + +These scripts are written with the intention of them being a tool to accelerate the maintenance +process, and as a regression test for feature integrations or bug fixes. + +These scripts depend heavily on the structure of html elements in the source code by using +xpath to locate certain elements. Therefore, make sure that all web elements in LRR source +code are unchanged for this script to run properly. And if any change is necessary, then make +sure to adjust its corresponding xpath on these scripts. + +Note: see "Writing history, number 4" the later part about xpath dependency is significantly reduced. + +IMPORTANT! +Do not change the order of the test cases in the file "test_suite.py", since many of the later test cases in the file +order depends on the execution of previous ones in the same file. If must change, make sure that you understand +the dependency between test cases completely. + +Contact us for any help or suggestions at: +Mr. Hui Lan (lanhui at zjnu dot edu dot cn) +Ibrahim M.I. Ismail (1525200991 at qq dot com) + +Writing history: +1- Feb 06, 2021: wrote the class skeleton. Ibrahim M.I. Ismail +2- Feb 07, 2021: implemented login(), create_course_portal() functions. Ibrahim M.I. Ismail +3- Feb 08, 2021: implemented post_lab_report(), manage_deadline() functions. Ibrahim M.I. Ismail +4- Mar 10, 2021: reduced the amount of web element locators that uses xpath by replacing finding elements + by xpath with IDs, as well introduced id attributes for some web elements on LRR's php files. +5- Mar 11, 2021: final fixes and enhancments. +""" + +from utility import MyUtility +from instructor import Instructor +from student import Student +from admin import Admin + +utility = MyUtility("http://127.0.0.1/edsa-LRR3/") +instructor = Instructor("aA124536!","202032070221", utility) +student = Student("aA124536!", "202032070222", utility) +admin = Admin("aA124536!","202032070221", utility) + +cond = 0 +f_name = utility.random_string(4) +l_name = utility.random_string(5) + +std_ids = ['202032020725', '202032020726', '202032020727', '202032020728', '202032020729'] + +def test_case_00(): + admin.batch_create_acc(std_ids) == cond + +def test_case_01(): + admin.create_new_account('202032020730') == cond + +def test_case_02(): + admin.account_block_activate('block') == cond + +def test_case_03(): + admin.account_block_activate('activate') == cond + +def test_case_04(): + utility.signup(f_name+' '+l_name, f_name+'.'+l_name+'@testing.com', 'aA124536!') == cond + +def test_case_05(): + instructor.create_course_portal() == cond + +def test_case_06(): + instructor.post_lab_report() == cond + +def test_case_07(): + student.join_course() == cond + +def test_case_08(): + student.submit_assignment() == cond + +def test_case_09(): + instructor.mark_submission() == cond + +def test_case_10(): + student.request_remarking() == cond + +def test_case_11(): + instructor.manage_deadline() == cond + +def test_case_12(): + instructor.create_course_portal() == cond + +def test_case_13(): + instructor.post_lab_report(group = 1) == cond + +def test_case_14(): + student.join_course() == cond + +def test_case_15(): + student.create_course_group() == cond + +def test_case_16(): + admin.assign_TA() == cond \ No newline at end of file diff --git a/test/SeleniumIbrahim/test_kit/utility.py b/test/SeleniumIbrahim/test_kit/utility.py new file mode 100644 index 0000000..57abc1c --- /dev/null +++ b/test/SeleniumIbrahim/test_kit/utility.py @@ -0,0 +1,304 @@ +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +import random +import datetime +import traceback + +class MyUtility: + + """This class is a utility class contains all the essential methods needed + for various functions that are not bond for a specific user. + + """ + + def __init__(self, page_url): + self.page_url = page_url + + def setPageURL(self, page_url): + + """This method sets a new page url, it might be used to initiate new webdriver + session with specific page of LRR in run-time. + + """ + self.page_url = page_url + + def getPageURL(self): + + """This method returns the page url provided when MyUtility object is created. + + Returns: + - page_url: string + + """ + return self.page_url + + def getSession(self): + """This method initiate new selenium webdriver session. + + Returns: + - driver: selenium.webdriver object. + 1 on failure to complete case execution. + + """ + try: + driver = webdriver.Chrome() + driver.get(self.getPageURL()) + return driver + except: + print("There was initiating a new session") + print("Error in \"getSession()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.log_error(err_msg) + print("Treminating session") + self.killSession(driver) + return 1 + + + def login(self, obj): + + """This method automates the login process for the instructor, it's needed for use + internally in order to carry on other automation operations that requires login beforehand. + + Returns: + - driver: logged in selenium.webdriver object. + 1 on failure to complete case execution. + + """ + try: + driver = self.getSession() + WebDriverWait(driver, 10) + username = driver.find_element(By.ID, "uname") + username.send_keys(obj.getStudentID()) + password = driver.find_element(By.ID, "upass") + password.send_keys(obj.getPassword()) + login = driver.find_element(By.ID, "log_btn") + login.click() + return driver + except: + print("There was a problem executing this test case") + print("Error in \"login()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.log_error(err_msg) + print("Treminating session") + self.killSession(driver) + return 1 + + def signup(self, name, email, password): + + """This method automates student registration process. + + Parameters: + - name: student name string. + - email: student email string. + - password: student password string. + + Returns: + 0 on success + 1 on failure to complete case execution. + + """ + try: + #Initiate a new webdriver session + driver = self.getSession() + wait = WebDriverWait(driver, 10, ignored_exceptions='StaleElementReferenceException') + + #Locate the signup form and fill in the student ID + signup_form = wait.until(EC.presence_of_element_located((By.ID, "signup_frm"))) + student_id = signup_form.find_element(By.ID, "std_id") + std_id = self.fetch_new_student_id() + student_id.send_keys(std_id) + next_btn = signup_form.find_element(By.ID, "next_btn") + next_btn.click() + + #Fill in student data and sign up. + wait2 = WebDriverWait(driver, 10, ignored_exceptions='StaleElementReferenceException') + reg_form = wait2.until(EC.presence_of_element_located((By.ID, "frm"))) + name_field = reg_form.find_element(By.XPATH, "//form/input[2]") + name_field.send_keys(name) + email_field = reg_form.find_element(By.XPATH, "//form/input[3]") + email_field.send_keys(email) + password_field = reg_form.find_element(By.XPATH, "//form/input[4]") + password_field.send_keys(password) + re_password_field = reg_form.find_element(By.XPATH, "//form/input[5]") + re_password_field.send_keys(password) + submit = reg_form.find_element(By.XPATH, "//form/input[6]") + submit.click() + return 0 + + except: + print("There was a problem executing this test case") + print("Error in \"signup()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.log_error(err_msg) + print("Treminating session") + self.killSession(driver) + return 1 + + def killSession(self, driver): + + """This method terminates the current running selenium webdriver session. + + """ + + driver.close() + + def getTime(self): + + """This method provides the now machine local time formated like "hhmmss" for example, + 18:30:40 is formatted as "183040". Needed to construct unique name strings. + + Returns: + - time: formatted machine local time. + """ + + time = datetime.datetime.today().strftime("%H%M%S") + return time + + def getTodayDate(self): + + """This method provides today's date formatted as "ddmmyy" for example, + 18/02/2021 is formatted as "18022021". Needed to construct unique name strings. + + Returns: + - date: formatted date string. + + """ + date = datetime.datetime.today().strftime ('%d%m%Y') + return date + + def getTomorrowDate(self): + + """This method provides tomorrow's date formatted as "ddmmyyyy" for example, + 18/02/2021 is formatted as "18022021". Needed to test extend deadlines function. + + Returns: + - date: formatted date string. + + """ + date = datetime.datetime.today() + datetime.timedelta(days=1) + date_str = date.strftime('%d%m%Y') + return date_str + + def getYesterdayDate(self): + + """This method provides yesterday's date formatted as "ddmmyyyy" for example, + 18/02/2021 is formatted as "18022021". Needed to test extend deadlines function. + + Returns: + - date: formatted date string. + + """ + date = datetime.datetime.today() - datetime.timedelta(days=1) + date_str = date.strftime('%d%m%Y') + return date_str + + def storeCourseCode(self, course_code): + + """This method stores the uniquely created course code from a combination of date and time + in a text file for reuse in other automation operations. + + """ + file = open("./course_code.txt","w") + file.write(course_code) + file.close() + + def getCourseCode(self): + + """This method retrives the stored course code from the text file to be used as reference + for various automation operations that requires access to a course portal. + + Returns: + - code: string. + """ + file = open("./course_code.txt", "r") + code = file.readline() + file.close() + return code + + def fetch_new_student_id(self): + + """This method provides pre-defined student ID in a file to be used + with signup automation operation. See "student_ids.txt" file for details. + + Returns: + - std_id: string + A student ID string from student_ids.txt file. + Note: the returned ID will be removed from the file. + + """ + + file = open("student_ids.txt", "r") + lines = file.readlines() + file.close() + + std_id = None + for line in lines: + if line[0] == "#": #skip comments in the file + continue + std_id = line + break + + if std_id != None: + #remove the recently fetched student ID from file + elem_idx = lines.index(std_id) # + del lines[elem_idx] + else: + #if all student IDs in file are exceeded, abort + print("No more student IDs available in file.") + return + + + #write the file lines back after removing the fetched student ID + file = open("student_ids.txt", "w+") + for line in lines: + file.write(line) + + file.close() + + return std_id.strip('\n') + + def open_course_page(self, driver): + """This method redirects the webdriver to a course page identified by its course code + fetched from 'course_code.txt' file. + + Paramaters: + driver: a selenium webdriver object. + + Returns: + 0 on success + 1 on failure to complete case execution. + + """ + try: + wait = WebDriverWait(driver, 10) + course_code = self.getCourseCode() + course_card = wait.until(EC.element_to_be_clickable((By.PARTIAL_LINK_TEXT,str(course_code)))) + course_card.click() + except: + print("There was a problem executing this test case") + print("Error in \"open_course_page()\" method, see error_log.txt for more details") + err_msg = traceback.format_exc() + self.log_error(err_msg) + print("Treminating session") + self.killSession(driver) + return 1 + + def log_error(self, error_msg): + """This method formats and writes various error messages retrieved from + the stacktrace into 'Error_log.txt' file for debugging. + """ + file = open("Error_log.txt","a") + time = datetime.datetime.today().strftime("%H:%M:%S") + date = datetime.datetime.today().strftime ('%d/%m/%Y') + file.write("[ERROR]"+date+"-"+time+">"+error_msg+"\n") + + def random_string(self, ch): + """Creates a random literal string of length ch. + """ + ls = [] + for i in range(ch): + ls.append(chr(random.randint(97,122))) + + return ''.join(ls) \ No newline at end of file