From cc5f08a16a061f2c1fbbbfcc5e27efa5aa003e76 Mon Sep 17 00:00:00 2001
From: 阳洁 <2077506045@qq.com>
Date: 星期三, 12 七月 2023 16:50:47 +0800
Subject: [PATCH] 测评系统自动测试

---
 po/home_page.py                        |   52 ++
 base/base_page.py                      |  154 +++++
 elements/login_elements.py             |   13 
 comm/my_random.py                      |   15 
 base/__init__.py                       |    0 
 elements/home_elements.py              |    8 
 elements/answer_elements.py            |   18 
 po/login_page.py                       |   65 ++
 elements/__init__.py                   |    0 
 report/report.html                     |  305 +++++++++++
 elements/test_package_list_elements.py |   75 ++
 po/share_add_page.py                   |   62 ++
 report/assets/style.css                |  186 +++++++
 elements/share_add_elements.py         |   22 
 main.py                                |   11 
 po/__init__.py                         |    0 
 testcase/test_smoking.py               |  168 ++++++
 po/test_package_list_page.py           |  285 ++++++++++
 testcase/__init__.py                   |    0 
 comm/__init__.py                       |    0 
 po/answer_page.py                      |   66 ++
 21 files changed, 1,505 insertions(+), 0 deletions(-)

diff --git a/base/__init__.py b/base/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/__init__.py
diff --git a/base/base_page.py b/base/base_page.py
new file mode 100644
index 0000000..fe27a1b
--- /dev/null
+++ b/base/base_page.py
@@ -0,0 +1,154 @@
+from selenium.common import TimeoutException
+from selenium.webdriver.support.wait import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+class BasePage:
+    # 鍒濆鍖栨柟娉�
+    def __init__(self, driver):
+        self.driver = driver
+
+    # 鍏冪礌瀹氫綅
+    def get_element(self, locator):
+        return self.driver.find_element(*locator)
+
+    # 澶氫釜鍏冪礌瀹氫綅
+    def get_elements(self, locator):
+        return self.driver.find_elements(*locator)
+
+    # 澶氫釜鍏冪礌瀹氫綅
+    def get_elements_wait(self, locator):
+        wait = WebDriverWait(self.driver, 10, 0.5)
+        wait.until(EC.presence_of_element_located(locator))
+        return self.driver.find_elements(*locator)
+
+    # 鍦ㄦ寚瀹氬厓绱犱腑瀹氫綅鍏冪礌
+    def get_element_in(self, element, locator):
+        return element.find_element(*locator)
+
+    # 鍦ㄦ寚瀹氬厓绱犱腑瀹氫綅澶氫釜鍏冪礌
+    def get_elements_in(self, element, *locator):
+        return element.find_elements(*locator)
+
+    # 鍏冪礌瀹氫綅
+    def get_element_wait(self, locator):
+        wait = WebDriverWait(self.driver, 10, 0.5)
+        wait.until(EC.presence_of_element_located(locator))
+        return self.driver.find_element(*locator)
+
+    # 浣跨敤js璇彞鐐瑰嚮
+    def script_click(self, *locator):
+        item = self.get_element(*locator)
+        self.driver.execute_script("arguments[0].click();", item)
+
+    def script_click_wait(self, *locator):
+        item = self.get_element_wait(*locator)
+        self.driver.execute_script("arguments[0].click();", item)
+
+    # 鐐瑰嚮
+    def click(self, locator):
+        """
+        鐐瑰嚮鍏冪礌
+        :param locator: 鍏冪礌瀹氫綅
+        :return:
+        """
+        self.get_element(locator).click()
+
+    def click_wait(self, *locator):
+        self.get_element_wait(*locator).click()
+
+    # 杈撳叆
+    def send_text(self, text, locator):
+        """
+        杈撳叆鍐呭
+        :param text: 鍐呭
+        :param locator: 鍏冪礌瀹氫綅
+        :return:
+        """
+        self.get_element(locator).send_keys(text)
+
+    def send_text_wait(self, text, *locator):
+        self.get_element_wait(*locator).send_keys(text)
+
+    # JavaScript鑾峰彇鍏冪礌鍊�
+    def get_input_value_js(self, locator):
+        input_box = self.get_element(locator)
+        input_value = self.driver.execute_script("return arguments[0].value", input_box)
+        return input_value
+
+    def get_input_value_js_wait(self, *locator):
+        input_box = self.get_element_wait(*locator)
+        input_value = self.driver.execute_script("return arguments[0].value", input_box)
+        return input_value
+
+    # 娓呴櫎
+    def clear_text(self, *locator):
+        self.get_element(*locator).clear()
+
+    def clear_text_wait(self, *locator):
+        self.get_element_wait(*locator).clear()
+
+    # 琛ㄥ崟鍒囨崲
+    def switch_iframe(self, locator):
+        self.driver.switch_to.frame(self.get_element(locator))
+
+    # 鍒囨崲鍒颁笂涓�灞傜殑iframe
+    def switch_parent_iframe(self):
+        self.driver.switch_to.parent_frame()
+
+    def switch_iframe_wait(self, locator):
+        self.driver.switch_to.frame(self.get_element_wait(locator))
+
+    # 绐楀彛鍒囨崲
+    def switch_window(self, n):
+        self.driver.switch_to.window(self.driver.window_handles[n])
+
+    # 妫�鏌ュ厓绱犳槸鍚﹀彲瑙佸彲鐐瑰嚮
+    def check_clickable(self, *locator):
+        wait = WebDriverWait(self.driver, 10, 0.5)
+        try:
+            e = wait.until(EC.element_to_be_clickable(locator))
+            if e is not None:
+                return True
+        except TimeoutException:
+            pass
+        return False
+
+    # 绛夊緟鍏冪礌鍑虹幇
+    def wait_appear(self, *locator):
+        wait = WebDriverWait(self.driver, 10, 0.5)
+        try:
+            e = wait.until(EC.presence_of_element_located(locator))
+            if e is not None:
+                return True
+        except TimeoutException:
+            pass
+        return False
+
+    # 婊戝姩椤甸潰
+    def scroll_by(self, num):
+        self.driver.execute_script("window.scrollBy(0, " + num + ");")
+
+    # 婊戝姩鍒板簳閮�
+    def scroll_to_bottom(self):
+        self.driver.execute_script("window.scrollBy(0,document.body.scrollHeight);")
+
+    # 婊戝姩鍒伴《閮�
+    def scroll_to_top(self):
+        self.driver.execute_script("window.scrollBy(0,0);")
+
+    # 鎴彇褰撳墠绐楀彛淇濆瓨涓哄浘鐗�
+    def get_png(self):
+        """鎴彇褰撳墠绐楀彛淇濆瓨涓哄浘鐗�"""
+        return self.driver.get_screenshot_as_png()
+
+    def open_new_table_to_url(self, url):
+        """
+        鎵撳紑涓�涓柊鐨勬爣绛鹃〉锛屽苟璺宠浆鍒版寚瀹氱殑url
+        :return:
+        """
+        # 鎵撳紑鏂版爣绛鹃〉
+        self.driver.execute_script("window.open()")
+        # 鍒囨崲鍒版柊鐨勬爣绛鹃〉
+        self.switch_window(-1)
+        # 鍦ㄦ柊鏍囩椤典腑鍔犺浇鎸囧畾url
+        self.driver.get(url)
diff --git a/comm/__init__.py b/comm/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/comm/__init__.py
diff --git a/comm/my_random.py b/comm/my_random.py
new file mode 100644
index 0000000..a4a1f34
--- /dev/null
+++ b/comm/my_random.py
@@ -0,0 +1,15 @@
+import random
+import string
+
+
+# 鐢熸垚闅忔満瀛楃涓�
+def random_string(length):
+    letters = string.ascii_letters + string.digits
+    return ''.join(random.choice(letters) for _ in range(length))
+
+
+# 鐢熸垚闅忔満閭鍦板潃
+def random_email(domain):
+    username = random_string(8)  # 鐢熸垚8涓瓧绗︾殑闅忔満鐢ㄦ埛鍚�
+    email = f"{username}@{domain}"  # 鏍规嵁闅忔満鐢ㄦ埛鍚嶅拰鎸囧畾鐨勫煙鍚嶇敓鎴愰偖绠卞湴鍧�
+    return email
diff --git a/elements/__init__.py b/elements/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/elements/__init__.py
diff --git a/elements/answer_elements.py b/elements/answer_elements.py
new file mode 100644
index 0000000..b25cee8
--- /dev/null
+++ b/elements/answer_elements.py
@@ -0,0 +1,18 @@
+from selenium.webdriver.common.by import By
+
+
+class AnswerElements:
+    # 棰樼洰閫夐」
+    options = (By.XPATH, "//span[@class='q-item-span-content']")
+    # 绂佺敤鐨勯�夐」
+    disabled = (By.XPATH, "//label[@aria-disabled='true']")
+    # 缁х画娴嬭瘯鎸夐挳
+    continue_testing_btn = (By.XPATH, "//button/span[text()='缁х画娴嬭瘯']")
+    # 鎻愪氦鎸夐挳
+    submit_btn = (By.XPATH, "//button/span[contains(text(),'鎻愪氦')]")
+    # 涓婁紶鎴愬姛鏍囬
+    upload_success_title = (By.XPATH, "//main/h2[contains(text(), '鏁版嵁宸茬粡鎴愬姛鍦颁笂浼犺嚦鏈嶅姟鍣�')]")
+
+    # 鍔犺浇妗嗘枃瀛�
+    loading_text = (By.XPATH, "//div[@class='el-loading-spinner']/p")
+
diff --git a/elements/home_elements.py b/elements/home_elements.py
new file mode 100644
index 0000000..5202115
--- /dev/null
+++ b/elements/home_elements.py
@@ -0,0 +1,8 @@
+from selenium.webdriver.common.by import By
+
+
+class HomeElements:
+    # 渚ц竟鏍忚彍鍗�
+    menu_ul = (By.XPATH, "//ul[@id='side-menu']")
+
+
diff --git a/elements/login_elements.py b/elements/login_elements.py
new file mode 100644
index 0000000..d19e2bd
--- /dev/null
+++ b/elements/login_elements.py
@@ -0,0 +1,13 @@
+from selenium.webdriver.common.by import By
+
+
+class LoginElements:
+    # 鐧诲綍鐢ㄦ埛鍚嶈緭鍏ユ
+    username_input = (By.XPATH, "//form//input[@name='username']")
+    # 鐧诲綍瀵嗙爜杈撳叆妗�
+    password_input = (By.XPATH, "//form//input[@name='password']")
+    # 楠岃瘉鐮佽緭鍏ユ
+    validate_code_input = (By.XPATH, "//form//input[@name='validateCode']")
+    # 鐧诲綍鎸夐挳
+    login_btn = (By.XPATH, "//form//button")
+
diff --git a/elements/share_add_elements.py b/elements/share_add_elements.py
new file mode 100644
index 0000000..8144912
--- /dev/null
+++ b/elements/share_add_elements.py
@@ -0,0 +1,22 @@
+from selenium.webdriver.common.by import By
+
+
+class ShareAddElements:
+    # 濮撳悕
+    name = (By.XPATH, "//input[@name='memberName']")
+    # 閭
+    email = (By.XPATH, "//textarea[@name='memberEmail']")
+    # 鎵嬫満鍙风爜
+    phone = (By.XPATH, "//textarea[@name='memberPhone']")
+    # 鎻愪氦鎸夐挳
+    submit_btn = (By.XPATH, "//button")
+
+    # 娴嬭瘯閾炬帴
+    test_url = (By.XPATH, "//a")
+
+    # 鍩烘湰淇℃伅-鐢靛瓙閭
+    fill_email = (By.XPATH, "//form/div[2]//input")
+    # 鍩烘湰淇℃伅-鑱屼綅
+    fill_position = (By.XPATH, "//form/div[3]//input")
+    # 鍩烘湰淇℃伅-閮ㄩ棬
+    fill_dept = (By.XPATH, "//form/div[4]//input")
diff --git a/elements/test_package_list_elements.py b/elements/test_package_list_elements.py
new file mode 100644
index 0000000..7d54cf8
--- /dev/null
+++ b/elements/test_package_list_elements.py
@@ -0,0 +1,75 @@
+from selenium.webdriver.common.by import By
+
+
+class TestPackageListElements:
+    # 娴嬭瘯鍖呭垪琛╥frame
+    this_iframe = (By.XPATH, "//iframe[@src='/exam/test_package']")
+    # 娣诲姞娴嬭瘯鍖卛frame
+    add_iframe = (By.XPATH, "//iframe[@src='/exam/test_package/add']")
+    # 浜у搧鍖呮爲iframe
+    prodTree_iframe = (By.XPATH, "//iframe[contains(@src,'/exam/product/getProdList')]")
+    # 鍒嗕韩iframe
+    share_iframe = (By.XPATH, "//iframe[contains(@src,'/exam/test_package/share')]")
+
+
+    # this-鍒涘缓鎸夐挳
+    create_btn = (By.XPATH, "//a[@onclick='$.operate.add()']")
+    # this-娴嬭瘯鍖呭垪琛ㄦ祴璇曞寘鍚嶇Оa鏍囩
+    table_packageName_a = (By.XPATH, "//table[@id='bootstrap-table']/tbody/tr/td[2]/a")
+    # this-鍒楄〃tr
+    table_package_tr = (By.XPATH, "//table[@id='bootstrap-table']/tbody/tr")
+    # this-鐢熸垚鍒嗕韩閾炬帴鎸夐挳
+    share_1_btn = (By.XPATH, "//a[@onclick='share(1)']")
+    # this-鍒犻櫎鎸夐挳
+    delete_btn = (By.XPATH, "//a[@onclick='$.operate.removeAll()']")
+    # this-鍒犻櫎纭鎸夐挳
+    delete_confirm_btn = (By.XPATH, "//div/a[text()='纭']")
+
+    # add-娴嬭瘯鍖呭悕绉�
+    add_testPackageName_input = (By.XPATH, "//input[@name='testName']")
+    # add-鎶ュ憡浼佷笟鍚嶇О
+    add_reportCompanyName_input = (By.XPATH, "//input[@name='reportCompanyName']")
+    # add-绫诲瀷
+    add_testPackageType_select = (By.XPATH, "//select[@name='testType']/../span")
+    # add-绫诲瀷閫夐」
+    add_testPackageType_option = (By.XPATH, "//ul[contains(@id,'select2-testType')]/li")
+    # add-鍖哄煙
+    add_testPackageArea_select = (By.XPATH, "//select[@name='testArea']/../span")
+    # add-鍖哄煙閫夐」
+    add_testPackageArea_option = (By.XPATH, "//ul[contains(@id,'select2-testArea')]/li")
+    # add-浜у搧鍖呮爲寮瑰嚭鎸夐挳
+    add_prodTree_btn = (By.XPATH, "//span[@onclick='selectProdTree()']")
+    # add-HR鎺ユ敹鎶ュ憡閭
+    add_hrEmail_input = (By.XPATH, "//input[@name='testEmail']")
+    # add-鏈夋晥鏈�
+    add_invalidTime = (By.XPATH, "//input[@name='invalidTime']")
+    # add-娴嬭瘯鑰呴個璇烽偖浠跺唴瀹规ā鏉�
+    add_templateLangType_select = (By.XPATH, "//select[@name='templateLangType']/../button")
+    # add-娴嬭瘯鑰呴個璇烽偖浠跺唴瀹规ā鏉块�夐」
+    add_templateLangType_option = (By.XPATH, "//select[@name='templateLangType']/../div/div/ul/li/a/span[2]")
+    # add-HR璇█绫诲瀷
+    add_hrTemplateLangType_select = (By.XPATH, "//select[@name='hrTemplateLangType']/../button")
+    # add-HR璇█绫诲瀷閫夐」
+    add_hrTemplateLangType_option = (By.XPATH, "//select[@name='hrTemplateLangType']/../div/div/ul/li/a/span[2]")
+    # add-鎶ュ憡妯℃澘
+    add_reportTemplateId_select = (By.XPATH, "//select[@name='reportTemplateId']/../button")
+    # add-鎶ュ憡妯℃澘閫夐」
+    add_reportTemplateId_option = (By.XPATH, "//select[@name='reportTemplateId']/../div/div/ul/li/a/span[2]")
+    # add-娴嬭瘯鑰呮帴鏀舵姤鍛�
+    add_autoSendReport_checkbox = (By.XPATH, "//input[@name='autoSendReport']")
+    # add-娴嬭瘯鑰呮姤鍛婅瑷�绫诲瀷
+    add_memberTemplateLangType_select = (By.XPATH, "//select[@name='memberTemplateLangType']/../button")
+    # add-娴嬭瘯鑰呮姤鍛婅瑷�绫诲瀷閫夐」
+    add_memberTemplateLangType_option = (By.XPATH, "//select[@name='memberTemplateLangType']/../div/div/ul/li/a/span[2]")
+    # add-纭鎸夐挳
+    add_confirm_btn = (By.XPATH, "//iframe[@src='/exam/test_package/add']/../../div[3]/a[1]")
+
+    # prodTree-浜у搧鍖呴�夐」
+    prodTree_option = (By.XPATH, "//div[@id='tree']/li/a/span[2]")
+    # proTree-纭鎸夐挳
+    prodTree_confirm_btn = (By.XPATH, "//iframe[contains(@src,'/exam/product/getProdList')]/../../div[3]/a[1]")
+
+    # share-url
+    share_url_input = (By.XPATH, "//input[@id='shareUrl']")
+    # share-鍏抽棴鎸夐挳
+    share_cancel_btn = (By.XPATH, "//iframe[contains(@src,'/exam/test_package/share')]/../../div[3]/a[2]")
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..bc98d09
--- /dev/null
+++ b/main.py
@@ -0,0 +1,11 @@
+import pytest
+import pytest_html
+from testcase import test_smoking
+
+
+def run_testcases():
+    pytest.main(['-vs', '--html=report/report.html', 'testcase/test_smoking.py'])
+
+
+if __name__ == '__main__':
+    run_testcases()
diff --git a/po/__init__.py b/po/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/po/__init__.py
diff --git a/po/answer_page.py b/po/answer_page.py
new file mode 100644
index 0000000..43e8297
--- /dev/null
+++ b/po/answer_page.py
@@ -0,0 +1,66 @@
+import random
+
+from base.base_page import BasePage
+from elements.answer_elements import AnswerElements as Answer
+import time
+
+
+class AnswerPage(BasePage):
+    """
+    绛旈椤甸潰
+    """
+    loading_count = 0
+
+    def answer_MAQ(self):
+        # 鑾峰彇鍒版墍鏈夐�夐」
+        time.sleep(3)
+
+        while True:
+
+            # 鍒ゆ柇鏄惁涓�鐩村瓨鍦ㄥ姞杞芥
+            if self.loadin_text():
+                return
+
+            try:
+                # 鐐瑰嚮缁х画娴嬭瘯鎸夐挳
+                self.click(Answer.continue_testing_btn)
+            except:
+                pass
+
+            try:
+                options = self.get_elements(Answer.options)
+                if len(options) < 6:
+                    continue
+                number = random.randint(0, 5)
+                options[number].click()
+            except:
+                pass
+
+            try:
+                # 鐐瑰嚮鎻愪氦鎸夐挳
+                self.click(Answer.submit_btn)
+            except:
+                pass
+
+            try:
+                # 鏁版嵁涓婁紶瀹屾垚
+                self.get_element(Answer.upload_success_title)
+                return
+            except:
+                pass
+
+    def loadin_text(self):
+        """
+        鍒ゆ柇鍔犺浇妗嗗姞杞斤紝濡傛灉鍔犺浇瓒呰繃10绉掔洿鎺ラ��鍑哄綋鍓嶆祴璇�
+        :return:
+        """
+        i = 0
+        while i < 10:
+            try:
+                self.get_element(Answer.loading_text)
+                i += 1
+                time.sleep(1)
+            except:
+                return False
+        else:
+            return True
diff --git a/po/home_page.py b/po/home_page.py
new file mode 100644
index 0000000..2802d29
--- /dev/null
+++ b/po/home_page.py
@@ -0,0 +1,52 @@
+from base.base_page import BasePage
+from selenium.webdriver.common.by import By
+import time
+
+
+class HomePage(BasePage):
+    """
+    涓婚〉闈晶杈规爮鑿滃崟涓巘ab鏍忔搷浣滈〉闈�
+    """
+
+    def menu_select(self, menu_text, is_first=True):
+        """
+        閫夋嫨鑿滃崟
+        :param menu_text: 鑿滃崟閫夐」鍐呭锛屼娇鐢� / 鍒嗛殧涓婁笅绾ц彍鍗�
+        :param is_first: 鏄惁涓轰竴绾ц彍鍗�,榛樿涓篢rue
+        :return:
+        """
+        # 鍒ゆ柇menu_text鏄惁鍖呭惈/
+        if "/" in menu_text:
+            # 鍖呭惈 / 鑾峰彇鍒扮涓�涓�/鍓嶇殑鑿滃崟閫夐」
+            # 鑾峰彇/绗竴娆″嚭鐜扮殑浣嶇疆
+            index = menu_text.find("/")
+            # 鎴彇瑕佺偣鍑荤殑鑿滃崟閫夐」
+            menu = menu_text[0:index]
+        else:
+            menu = menu_text
+
+        # 鍒ゆ柇鏄惁涓�绾ц彍鍗�
+        if is_first:
+            # 鑿滃崟鍏冪礌瀹氫綅
+            menu_loc = (By.XPATH, "//ul[@id='side-menu']//span[text()='" + menu + "']")
+            # 鑾峰彇鑿滃崟鍏冪礌
+            menu_element = self.get_element(menu_loc)
+            # 鐐瑰嚮鑿滃崟
+            menu_element.click()
+            time.sleep(1)
+        else:
+            # 鑿滃崟鍏冪礌瀹氫綅
+            menu_loc = (By.XPATH, "//ul[@id='side-menu']//a[text()='" + menu + "']")
+            # 鑾峰彇鑿滃崟鍏冪礌
+            menu_element = self.get_element(menu_loc)
+            # 鐐瑰嚮鑿滃崟
+            menu_element.click()
+            time.sleep(1)
+
+        if "/" in menu_text:
+            # 鑾峰彇/绗竴娆″嚭鐜扮殑浣嶇疆
+            index = menu_text.find("/")
+            # 鎴彇鍓╀綑鐨勮彍鍗曞唴瀹�
+            menu_text = menu_text[index + 1: len(menu_text)]
+            # 閫掑綊璋冪敤
+            self.menu_select(menu_text, False)
diff --git a/po/login_page.py b/po/login_page.py
new file mode 100644
index 0000000..08b28b2
--- /dev/null
+++ b/po/login_page.py
@@ -0,0 +1,65 @@
+from base.base_page import BasePage
+from elements.login_elements import LoginElements as Login
+
+
+class LoginPage(BasePage):
+    """
+    鐧诲綍椤甸潰
+    """
+
+    def username_input(self, username):
+        """
+        濉啓鐢ㄦ埛鍚�
+        :param username: 鐢ㄦ埛鍚�
+        :return:
+        """
+        self.send_text(username, Login.username_input)
+
+    def password_input(self, password):
+        """
+        濉啓瀵嗙爜
+        :param password: 瀵嗙爜
+        :return:
+        """
+        self.send_text(password, Login.password_input)
+
+    def validate_code_input(self, validate_code):
+        """
+        濉啓楠岃瘉鐮�
+        :param validate_code: 楠岃瘉鐮�
+        :return:
+        """
+        self.send_text(validate_code, Login.validate_code_input)
+
+    def login_btn(self):
+        """
+        鐐瑰嚮鐧诲綍鎸夐挳
+        :return:
+        """
+        self.click(Login.login_btn)
+
+    def login(self, username, password, validate_code):
+        """
+        鐧诲綍
+        :param username: 鐢ㄦ埛鍚�
+        :param password: 瀵嗙爜
+        :param validate_code: 楠岃瘉鐮�
+        :return:
+        """
+        self.username_input(username)
+        self.password_input(password)
+        self.validate_code_input(validate_code)
+        self.login_btn()
+
+    def is_login_success(self):
+        """
+        鍒ゆ柇鏄惁鐧诲綍鎴愬姛
+        :return: bool
+        """
+        current_url = self.driver.current_url
+        if "index" in self.driver.current_url:
+            return True
+        else:
+            return False
+
+
diff --git a/po/share_add_page.py b/po/share_add_page.py
new file mode 100644
index 0000000..7642306
--- /dev/null
+++ b/po/share_add_page.py
@@ -0,0 +1,62 @@
+from base.base_page import BasePage
+from elements.share_add_elements import ShareAddElements as Share
+import time
+
+
+class ShareAddPage(BasePage):
+    """
+    鍒嗕韩閾炬帴鎵撳紑鐨勯〉闈�
+    """
+    def name_input(self, name):
+        """
+        杈撳叆濮撳悕
+        :param name:
+        :return:
+        """
+        self.send_text(name, Share.name)
+
+    def email_input(self, email):
+        """
+        杈撳叆閭
+        :param email:
+        :return:
+        """
+        self.send_text(email, Share.email)
+
+    def submit_btn(self):
+        """
+        鐐瑰嚮鎻愪氦
+        :return:
+        """
+        self.click_wait(Share.submit_btn)
+
+    def page_default_operation(self, name, email):
+        """
+        椤甸潰榛樿鎿嶄綔
+        :param name:
+        :param email:
+        :return:
+        """
+        # 杈撳叆濮撳悕
+        self.name_input(name)
+        # 杈撳叆閭
+        self.email_input(email)
+        # 鐐瑰嚮鎻愪氦
+        self.submit_btn()
+        # 鑾峰彇娴嬭瘯閾炬帴骞惰繑鍥�
+        return self.get_element_wait(Share.test_url).text
+
+    def fill_info(self, email, position, dept):
+        """
+        濉啓鍩烘湰淇℃伅
+        :param email: 閭
+        :param position: 鑱屼綅
+        :param dept: 閮ㄩ棬
+        :return:
+        """
+        self.send_text_wait(email, Share.fill_email)
+        self.send_text_wait(position, Share.fill_position)
+        self.send_text_wait(dept, Share.fill_dept)
+        self.click(Share.submit_btn)
+        time.sleep(2)
+
diff --git a/po/test_package_list_page.py b/po/test_package_list_page.py
new file mode 100644
index 0000000..0ee54db
--- /dev/null
+++ b/po/test_package_list_page.py
@@ -0,0 +1,285 @@
+from base.base_page import BasePage
+from selenium.webdriver.common.by import By
+from elements.test_package_list_elements import TestPackageListElements as PackageList
+import time
+
+
+class TestPackageListPage(BasePage):
+    """
+    娴嬭瘯鍖呭垪琛ㄩ〉闈�
+    """
+
+    def __init__(self, driver):
+        """鍒濆鍖栨椂灏唅frame鍒囨崲鍒板綋鍓嶉〉闈�"""
+        super().__init__(driver)
+        try:
+            self.driver.switch_to.default_content()
+            self.switch_iframe(PackageList.this_iframe)
+        except:
+            pass
+
+    def create_btn(self):
+        """
+        鐐瑰嚮鍒涘缓鎸夐挳
+        :return:
+        """
+        self.click(PackageList.create_btn)
+        # 鍒囨崲鍒癮dd绐楀彛鐨刬frame
+        self.switch_iframe(PackageList.add_iframe)
+
+    def delete_package(self):
+        """
+        鍒犻櫎娴嬭瘯鍖�
+        :return:
+        """
+        # 鐐瑰嚮鍒犻櫎鎸夐挳
+        self.click(PackageList.delete_btn)
+        # 鐐瑰嚮纭鎸夐挳
+        self.click_wait(PackageList.delete_confirm_btn)
+
+    def testPackageName_input(self, testPackageName):
+        """
+        杈撳叆娴嬭瘯鍖呭悕绉�
+        :param testPackageName: 娴嬭瘯鍖呭悕绉�
+        :return:
+        """
+        self.send_text(testPackageName, PackageList.add_testPackageName_input)
+
+    def reportCompanyName_input(self, reportCompanyName):
+        """
+        杈撳叆鎶ュ憡浼佷笟鍚嶇О
+        :param reportCompanyName: 鎶ュ憡浼佷笟鍚嶇О
+        :return:
+        """
+        self.send_text(reportCompanyName, PackageList.add_reportCompanyName_input)
+
+    def testPackageType_select(self, testPackageType):
+        """
+        閫夋嫨娴嬭瘯鍖呯被鍨�
+        :param testPackageType:
+        :return:
+        """
+        # 鐐瑰嚮涓嬫媺妗�
+        self.click(PackageList.add_testPackageType_select)
+        # 鑾峰彇鎵�鏈夐�夐」
+        options = self.get_elements(PackageList.add_testPackageType_option)
+        # 鍒ゆ柇閫夐」鏄惁涓庝紶鍏ョ殑閫夐」鐩哥瓑
+        for opt in options:
+            if opt.text == testPackageType:
+                # 鐩哥瓑鍒欑偣鍑昏閫夐」
+                opt.click()
+                break
+
+    def prodTree_select(self, prodName):
+        """
+        閫夋嫨浜у搧鍖�
+        :param prodName:
+        :return:
+        """
+        # 鐐瑰嚮鏌ヨ鎸夐挳
+        self.click(PackageList.add_prodTree_btn)
+        # 鍒囨崲鍒皃rodTree鐨刬frame
+        self.switch_iframe_wait(PackageList.prodTree_iframe)
+        time.sleep(2)
+        # 鑾峰彇鎵�鏈夐�夐」
+        options = self.get_elements(PackageList.prodTree_option)
+        # 鍒ゆ柇閫夐」鏄惁涓庝紶鍏ラ�夐」鐩哥瓑
+        for opt in options:
+            if opt.text == prodName:
+                opt.click()
+                break
+        # 鍒囨崲鍒颁笂涓�灞傜殑iframe
+        self.switch_parent_iframe()
+        # 鐐瑰嚮纭鎸夐挳
+        self.click(PackageList.prodTree_confirm_btn)
+
+    def hrReportEmail_input(self, hr_email):
+        """
+        杈撳叆HR鎺ユ敹鎶ュ憡閭
+        :param hr_email:
+        :return:
+        """
+        self.send_text(hr_email, PackageList.add_hrEmail_input)
+
+    def invalidTime_input(self, invalid_time):
+        """
+        杈撳叆鏈夋晥鏈�
+        :param invalid_time:
+        :return:
+        """
+        self.send_text(invalid_time, PackageList.add_invalidTime)
+        self.click(PackageList.add_invalidTime)
+
+    def testTemplateLangType_select(self, text):
+        """
+        閫夋嫨娴嬭瘯鑰呴個璇烽偖浠跺唴瀹规ā鏉�
+        :param text: 璇█绫诲瀷锛屽彲浠ラ�夋嫨澶氫釜锛屼娇鐢� , 鍒嗛殧
+        :return:
+        """
+        # 鍒嗗壊text
+        lang_types = text.split(",")
+        # 鐐瑰嚮涓嬫媺妗�
+        self.click(PackageList.add_templateLangType_select)
+        # 鑾峰彇鎵�鏈夐�夐」
+        options = self.get_elements(PackageList.add_templateLangType_option)
+        # 鍒ゆ柇閫夐」鏄惁涓庝紶鍏ョ殑閫夐」鐩哥瓑
+        for opt in options:
+            for lang_type in lang_types:
+                if opt.text == lang_type:
+                    # 鐩哥瓑鍒欑偣鍑昏閫夐」
+                    opt.click()
+                    break
+
+    def hrTemplateLangType_select(self, text):
+        """
+        閫夋嫨HR閭欢鍐呭妯℃澘
+        :param text: 璇█绫诲瀷锛屽彲浠ラ�夋嫨澶氫釜锛屼娇鐢� , 鍒嗛殧
+        :return:
+        """
+        # 鍒嗗壊text
+        lang_types = text.split(",")
+        # 鐐瑰嚮涓嬫媺妗�
+        self.click(PackageList.add_hrTemplateLangType_select)
+        # 鑾峰彇鎵�鏈夐�夐」
+        options = self.get_elements(PackageList.add_hrTemplateLangType_option)
+        # 鍒ゆ柇閫夐」鏄惁涓庝紶鍏ョ殑閫夐」鐩哥瓑
+        for opt in options:
+            for lang_type in lang_types:
+                if opt.text == lang_type:
+                    # 鐩哥瓑鍒欑偣鍑昏閫夐」
+                    opt.click()
+                    break
+
+    def reportTemplate_select(self, text):
+        """
+        鎶ュ憡妯℃澘閫夋嫨
+        :param text: 妯℃澘锛屽彲浠ラ�夋嫨澶氫釜锛屼娇鐢� , 鍒嗛殧
+        :return:
+        """
+        # 鍒嗗壊text
+        templates = text.split(",")
+        # 鐐瑰嚮涓嬫媺妗�
+        self.click(PackageList.add_reportTemplateId_select)
+        # 鑾峰彇鎵�鏈夐�夐」
+        options = self.get_elements(PackageList.add_reportTemplateId_option)
+        # 鍒ゆ柇閫夐」鏄惁涓庝紶鍏ョ殑閫夐」鐩哥瓑
+        for opt in options:
+            for template in templates:
+                if opt.text == template:
+                    # 鐩哥瓑鍒欑偣鍑昏閫夐」
+                    opt.click()
+                    break
+        # 鍐嶆鐐瑰嚮涓嬫媺妗�
+        self.click(PackageList.add_reportTemplateId_select)
+
+    def autoSendReport_checkbox(self):
+        """
+        鐐瑰嚮娴嬭瘯鑰呮帴鏀舵姤鍛�
+        :return:
+        """
+        self.click(PackageList.add_autoSendReport_checkbox)
+
+    def memberReportLangType_select(self, text):
+        """
+        娴嬭瘯鑰呮姤鍛婇偖浠惰瑷�绫诲瀷
+        :param text: 璇█绫诲瀷锛屽彲浠ラ�夋嫨澶氫釜锛屼娇鐢� , 鍒嗛殧
+        :return:
+        """
+        # 鍒嗗壊text
+        lang_types = text.split(",")
+        # 鐐瑰嚮涓嬫媺妗�
+        self.click(PackageList.add_memberTemplateLangType_select)
+        # 鑾峰彇鎵�鏈夐�夐」
+        options = self.get_elements(PackageList.add_memberTemplateLangType_option)
+        # 鍒ゆ柇閫夐」鏄惁涓庝紶鍏ョ殑閫夐」鐩哥瓑
+        for opt in options:
+            for lang_type in lang_types:
+                if opt.text == lang_type:
+                    # 鐩哥瓑鍒欑偣鍑昏閫夐」
+                    opt.click()
+                    break
+
+    def add_confirm(self):
+        """
+        鐐瑰嚮纭鎸夐挳
+        :return:
+        """
+        # 鍥炲埌涓婁竴灞俰frame
+        self.switch_parent_iframe()
+        self.click(PackageList.add_confirm_btn)
+
+    def is_create_success(self, packageName):
+        """
+        鍒ゆ柇杩欎釜娴嬭瘯鍖呮槸鍚﹀垱寤烘垚鍔�
+        :param packageName: 娴嬭瘯鍖呭悕绉�
+        :return: bool
+        """
+        names = self.get_elements(PackageList.table_packageName_a)
+        for name in names:
+            if name.text == packageName:
+                return True
+        return False
+
+    def click_package_checkbox(self, packageName):
+        """
+        閫変腑娴嬭瘯鍖咃紝鍕鹃�夊搴旀祴璇曞寘鍒楄〃鐨勫閫夋
+        :param packageName: 娴嬭瘯鍖呭悕绉�
+        :return:
+        """
+        # 鑾峰彇鍒皌able鐨勬墍鏈塼r
+        table_trs = self.get_elements(PackageList.table_package_tr)
+        for tr in table_trs:
+            # 娴嬭瘯鍖呭悕绉板湪tr涓殑瀹氫綅
+            package_name_loc = (By.XPATH, "td[2]/a")
+            # 鑾峰彇tr涓殑娴嬭瘯鍖呭悕绉�
+            tr_package_name = self.get_element_in(tr, package_name_loc).text
+            if tr_package_name == packageName:
+                # 澶嶉�夋鍦╰r涓殑瀹氫綅
+                package_checkbox_loc = (By.XPATH, "td[1]/input")
+                # 鑾峰彇澶嶉�夋鍏冪礌
+                tr_checkbox = self.get_element_in(tr, package_checkbox_loc)
+                # 鐐瑰嚮澶嶉�夋
+                tr_checkbox.click()
+                break
+
+    def click_share_1_btn(self):
+        """
+        鐐瑰嚮鐢熸垚鍒嗕韩閾炬帴鎸夐挳
+        :return:
+        """
+        self.click(PackageList.share_1_btn)
+
+    def get_share_url(self):
+        """
+        鑾峰彇鍒嗕韩閾炬帴
+        :return: url
+        """
+        # 鍒囨崲鍒皊hare鐨刬frame涓�
+        self.switch_iframe(PackageList.share_iframe)
+        # 鑾峰彇url
+        url = self.get_input_value_js(PackageList.share_url_input)
+        # 閫�鍥炰笂涓�灞俰frame
+        self.switch_parent_iframe()
+        # 鐐瑰嚮鍏抽棴鎸夐挳
+        self.click(PackageList.share_cancel_btn)
+        return url
+
+
+    def selector(self, click_loc, options_loc, text):
+        """
+        閫氱敤閫夋嫨鍣紝鐩存帴璋冪敤閫夋嫨涓嬫媺妗嗛�夐」
+        :param click_loc: 鐐瑰嚮鍚庡嚭鐜颁笅鎷夐�夐」鐨勫厓绱犲畾浣�
+        :param options_loc: 閫夐」鐨勫厓绱犲畾浣�
+        :param text: 鍒ゆ柇鐐瑰嚮鐨勬枃鏈�
+        :return:
+        """
+        # 鐐瑰嚮涓嬫媺妗�
+        self.click(click_loc)
+        # 鑾峰彇鎵�鏈夐�夐」
+        options = self.get_elements(options_loc)
+        # 鍒ゆ柇閫夐」鏄惁涓庝紶鍏ョ殑閫夐」鐩哥瓑
+        for opt in options:
+            if opt.text == text:
+                # 鐩哥瓑鍒欑偣鍑昏閫夐」
+                opt.click()
+                break
diff --git a/report/assets/style.css b/report/assets/style.css
new file mode 100644
index 0000000..3edac88
--- /dev/null
+++ b/report/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/report/report.html b/report/report.html
new file mode 100644
index 0000000..5635d9d
--- /dev/null
+++ b/report/report.html
@@ -0,0 +1,305 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>report.html</title>
+    <link href="assets/style.css" rel="stylesheet" type="text/css"/></head>
+  <body onLoad="init()">
+    <script>/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+function toArray(iter) {
+    if (iter === null) {
+        return null;
+    }
+    return Array.prototype.slice.call(iter);
+}
+
+function find(selector, elem) { // eslint-disable-line no-redeclare
+    if (!elem) {
+        elem = document;
+    }
+    return elem.querySelector(selector);
+}
+
+function findAll(selector, elem) {
+    if (!elem) {
+        elem = document;
+    }
+    return toArray(elem.querySelectorAll(selector));
+}
+
+function sortColumn(elem) {
+    toggleSortStates(elem);
+    const colIndex = toArray(elem.parentNode.childNodes).indexOf(elem);
+    let key;
+    if (elem.classList.contains('result')) {
+        key = keyResult;
+    } else if (elem.classList.contains('links')) {
+        key = keyLink;
+    } else {
+        key = keyAlpha;
+    }
+    sortTable(elem, key(colIndex));
+}
+
+function showAllExtras() { // eslint-disable-line no-unused-vars
+    findAll('.col-result').forEach(showExtras);
+}
+
+function hideAllExtras() { // eslint-disable-line no-unused-vars
+    findAll('.col-result').forEach(hideExtras);
+}
+
+function showExtras(colresultElem) {
+    const extras = colresultElem.parentNode.nextElementSibling;
+    const expandcollapse = colresultElem.firstElementChild;
+    extras.classList.remove('collapsed');
+    expandcollapse.classList.remove('expander');
+    expandcollapse.classList.add('collapser');
+}
+
+function hideExtras(colresultElem) {
+    const extras = colresultElem.parentNode.nextElementSibling;
+    const expandcollapse = colresultElem.firstElementChild;
+    extras.classList.add('collapsed');
+    expandcollapse.classList.remove('collapser');
+    expandcollapse.classList.add('expander');
+}
+
+function showFilters() {
+    let visibleString = getQueryParameter('visible') || 'all';
+    visibleString = visibleString.toLowerCase();
+    const checkedItems = visibleString.split(',');
+
+    const filterItems = document.getElementsByClassName('filter');
+    for (let i = 0; i < filterItems.length; i++) {
+        filterItems[i].hidden = false;
+
+        if (visibleString != 'all') {
+            filterItems[i].checked = checkedItems.includes(filterItems[i].getAttribute('data-test-result'));
+            filterTable(filterItems[i]);
+        }
+    }
+}
+
+function addCollapse() {
+    // Add links for show/hide all
+    const resulttable = find('table#results-table');
+    const showhideall = document.createElement('p');
+    showhideall.innerHTML = '<a href="javascript:showAllExtras()">Show all details</a> / ' +
+                            '<a href="javascript:hideAllExtras()">Hide all details</a>';
+    resulttable.parentElement.insertBefore(showhideall, resulttable);
+
+    // Add show/hide link to each result
+    findAll('.col-result').forEach(function(elem) {
+        const collapsed = getQueryParameter('collapsed') || 'Passed';
+        const extras = elem.parentNode.nextElementSibling;
+        const expandcollapse = document.createElement('span');
+        if (extras.classList.contains('collapsed')) {
+            expandcollapse.classList.add('expander');
+        } else if (collapsed.includes(elem.innerHTML)) {
+            extras.classList.add('collapsed');
+            expandcollapse.classList.add('expander');
+        } else {
+            expandcollapse.classList.add('collapser');
+        }
+        elem.appendChild(expandcollapse);
+
+        elem.addEventListener('click', function(event) {
+            if (event.currentTarget.parentNode.nextElementSibling.classList.contains('collapsed')) {
+                showExtras(event.currentTarget);
+            } else {
+                hideExtras(event.currentTarget);
+            }
+        });
+    });
+}
+
+function getQueryParameter(name) {
+    const match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
+    return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
+}
+
+function init () { // eslint-disable-line no-unused-vars
+    resetSortHeaders();
+
+    addCollapse();
+
+    showFilters();
+
+    sortColumn(find('.initial-sort'));
+
+    findAll('.sortable').forEach(function(elem) {
+        elem.addEventListener('click',
+            function() {
+                sortColumn(elem);
+            }, false);
+    });
+}
+
+function sortTable(clicked, keyFunc) {
+    const rows = findAll('.results-table-row');
+    const reversed = !clicked.classList.contains('asc');
+    const sortedRows = sort(rows, keyFunc, reversed);
+    /* Whole table is removed here because browsers acts much slower
+     * when appending existing elements.
+     */
+    const thead = document.getElementById('results-table-head');
+    document.getElementById('results-table').remove();
+    const parent = document.createElement('table');
+    parent.id = 'results-table';
+    parent.appendChild(thead);
+    sortedRows.forEach(function(elem) {
+        parent.appendChild(elem);
+    });
+    document.getElementsByTagName('BODY')[0].appendChild(parent);
+}
+
+function sort(items, keyFunc, reversed) {
+    const sortArray = items.map(function(item, i) {
+        return [keyFunc(item), i];
+    });
+
+    sortArray.sort(function(a, b) {
+        const keyA = a[0];
+        const keyB = b[0];
+
+        if (keyA == keyB) return 0;
+
+        if (reversed) {
+            return keyA < keyB ? 1 : -1;
+        } else {
+            return keyA > keyB ? 1 : -1;
+        }
+    });
+
+    return sortArray.map(function(item) {
+        const index = item[1];
+        return items[index];
+    });
+}
+
+function keyAlpha(colIndex) {
+    return function(elem) {
+        return elem.childNodes[1].childNodes[colIndex].firstChild.data.toLowerCase();
+    };
+}
+
+function keyLink(colIndex) {
+    return function(elem) {
+        const dataCell = elem.childNodes[1].childNodes[colIndex].firstChild;
+        return dataCell == null ? '' : dataCell.innerText.toLowerCase();
+    };
+}
+
+function keyResult(colIndex) {
+    return function(elem) {
+        const strings = ['Error', 'Failed', 'Rerun', 'XFailed', 'XPassed',
+            'Skipped', 'Passed'];
+        return strings.indexOf(elem.childNodes[1].childNodes[colIndex].firstChild.data);
+    };
+}
+
+function resetSortHeaders() {
+    findAll('.sort-icon').forEach(function(elem) {
+        elem.parentNode.removeChild(elem);
+    });
+    findAll('.sortable').forEach(function(elem) {
+        const icon = document.createElement('div');
+        icon.className = 'sort-icon';
+        icon.textContent = 'vvv';
+        elem.insertBefore(icon, elem.firstChild);
+        elem.classList.remove('desc', 'active');
+        elem.classList.add('asc', 'inactive');
+    });
+}
+
+function toggleSortStates(elem) {
+    //if active, toggle between asc and desc
+    if (elem.classList.contains('active')) {
+        elem.classList.toggle('asc');
+        elem.classList.toggle('desc');
+    }
+
+    //if inactive, reset all other functions and add ascending active
+    if (elem.classList.contains('inactive')) {
+        resetSortHeaders();
+        elem.classList.remove('inactive');
+        elem.classList.add('active');
+    }
+}
+
+function isAllRowsHidden(value) {
+    return value.hidden == false;
+}
+
+function filterTable(elem) { // eslint-disable-line no-unused-vars
+    const outcomeAtt = 'data-test-result';
+    const outcome = elem.getAttribute(outcomeAtt);
+    const classOutcome = outcome + ' results-table-row';
+    const outcomeRows = document.getElementsByClassName(classOutcome);
+
+    for(let i = 0; i < outcomeRows.length; i++){
+        outcomeRows[i].hidden = !elem.checked;
+    }
+
+    const rows = findAll('.results-table-row').filter(isAllRowsHidden);
+    const allRowsHidden = rows.length == 0 ? true : false;
+    const notFoundMessage = document.getElementById('not-found-message');
+    notFoundMessage.hidden = !allRowsHidden;
+}
+</script>
+    <h1>report.html</h1>
+    <p>Report generated on 12-Jul-2023 at 16:29:07 by <a href="https://pypi.python.org/pypi/pytest-html">pytest-html</a> v3.2.0</p>
+    <h2>Summary</h2>
+    <p>4 tests ran in 65.45 seconds. </p>
+    <p class="filter" hidden="true">(Un)check the boxes to filter the results.</p><input checked="true" class="filter" data-test-result="passed" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="passed">4 passed</span>, <input checked="true" class="filter" data-test-result="skipped" disabled="true" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="skipped">0 skipped</span>, <input checked="true" class="filter" data-test-result="failed" disabled="true" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="failed">0 failed</span>, <input checked="true" class="filter" data-test-result="error" disabled="true" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="error">0 errors</span>, <input checked="true" class="filter" data-test-result="xfailed" disabled="true" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="xfailed">0 expected failures</span>, <input checked="true" class="filter" data-test-result="xpassed" disabled="true" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="xpassed">0 unexpected passes</span>
+    <h2>Results</h2>
+    <table id="results-table">
+      <thead id="results-table-head">
+        <tr>
+          <th class="sortable result initial-sort" col="result">Result</th>
+          <th class="sortable" col="name">Test</th>
+          <th class="sortable" col="duration">Duration</th>
+          <th class="sortable links" col="links">Links</th></tr>
+        <tr hidden="true" id="not-found-message">
+          <th colspan="4">No results found. Try to check the filters</th></tr></thead>
+      <tbody class="passed results-table-row">
+        <tr>
+          <td class="col-result">Passed</td>
+          <td class="col-name">testcase/test_smoking.py::TestSmoking::test_login</td>
+          <td class="col-duration">4.52</td>
+          <td class="col-links"></td></tr>
+        <tr>
+          <td class="extra" colspan="4">
+            <div class="empty log">No log output captured.</div></td></tr></tbody>
+      <tbody class="passed results-table-row">
+        <tr>
+          <td class="col-result">Passed</td>
+          <td class="col-name">testcase/test_smoking.py::TestSmoking::test_create_package</td>
+          <td class="col-duration">8.38</td>
+          <td class="col-links"></td></tr>
+        <tr>
+          <td class="extra" colspan="4">
+            <div class="empty log">No log output captured.</div></td></tr></tbody>
+      <tbody class="passed results-table-row">
+        <tr>
+          <td class="col-result">Passed</td>
+          <td class="col-name">testcase/test_smoking.py::TestSmoking::test_open_url</td>
+          <td class="col-duration">6.30</td>
+          <td class="col-links"></td></tr>
+        <tr>
+          <td class="extra" colspan="4">
+            <div class="empty log">No log output captured.</div></td></tr></tbody>
+      <tbody class="passed results-table-row">
+        <tr>
+          <td class="col-result">Passed</td>
+          <td class="col-name">testcase/test_smoking.py::TestSmoking::test_answer</td>
+          <td class="col-duration">46.21</td>
+          <td class="col-links"></td></tr>
+        <tr>
+          <td class="extra" colspan="4">
+            <div class="empty log">No log output captured.</div></td></tr></tbody></table></body></html>
\ No newline at end of file
diff --git a/testcase/__init__.py b/testcase/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testcase/__init__.py
diff --git a/testcase/test_smoking.py b/testcase/test_smoking.py
new file mode 100644
index 0000000..5c304cf
--- /dev/null
+++ b/testcase/test_smoking.py
@@ -0,0 +1,168 @@
+from datetime import datetime
+import multiprocessing
+import threading
+import time
+
+import pytest
+from selenium import webdriver
+from selenium.webdriver.chrome.options import Options
+from po.login_page import LoginPage
+from po.home_page import HomePage
+from po.test_package_list_page import TestPackageListPage
+from po.share_add_page import ShareAddPage
+from po.answer_page import AnswerPage
+from comm.my_random import *
+from selenium.common import WebDriverException
+from selenium.common.exceptions import ElementClickInterceptedException, TimeoutException
+import HTMLTestRunner
+
+
+class TestSmoking:
+    driver = None
+    test_package_name = "MAQ鑷姩娴嬭瘯鍖�-20230711151825"
+
+    def test_login(self):
+        """
+        鎵撳紑椤甸潰骞惰繘琛岀櫥褰曟祴璇�
+        :return:
+        """
+        TestSmoking.driver = webdriver.Chrome()
+        driver = TestSmoking.driver
+        login_page = LoginPage(driver)
+        driver.get("http://47.114.179.216:8090/login?lang=zh_CN")
+        driver.maximize_window()
+        login_page.login("admin", "HyNc#jp0z#N7Y5ih", "ots")
+        time.sleep(2)
+        assert login_page.is_login_success()
+
+    def test_create_package(self):
+        """
+        鍒涘缓娴嬭瘯鍖�
+        :return:
+        """
+        # 鑾峰彇driver
+        driver = TestSmoking.driver
+        # 鍒涘缓home椤甸潰鐨勫璞�
+        home_page = HomePage(driver)
+        # 鐐瑰嚮宸︿晶鑿滃崟杩涘叆椤甸潰
+        home_page.menu_select("娴嬭瘯鍖呯鐞�/娴嬭瘯鍖呭垪琛�")
+
+        # 鍒涘缓娴嬭瘯鍖呭垪琛ㄩ〉闈㈢殑瀵硅薄
+        test_package_list = TestPackageListPage(driver)
+        # 鐐瑰嚮鍒涘缓鎸夐挳
+        test_package_list.create_btn()
+        # 鑾峰彇褰撳墠鏃堕棿
+        current_time = datetime.now()
+        # 杞崲涓哄瓧绗︿覆
+        time_str = current_time.strftime("%Y%m%d%H%M%S")
+        # 鍒涘缓娴嬭瘯鍖呭~鍐欑殑鍙傛暟
+        # 娴嬭瘯鍖呭悕绉�
+        test_package_name = "MAQ鑷姩娴嬭瘯鍖�-" + time_str
+        # 璁剧疆娴嬭瘯鍖呭悕绉颁负绫诲彉閲�
+        TestSmoking.test_package_name = test_package_name
+        # 鎶ュ憡浼佷笟鍚嶇О
+        report_company_name = "TAI娴嬭瘯"
+        # 娴嬭瘯鍖呯被鍨�
+        test_package_type = "浜烘牸娴嬭瘯鍖�"
+        # 浜у搧鍖�
+        prod_name = "MAQV2"
+        # HR閭
+        hr_email = "2077506045@qq.com"
+        # 鏈夋晥鏈�
+        invalid_time = "2024-01-01 00:00:00"
+        # 娴嬭瘯鑰呴個璇烽偖浠惰瑷�绫诲瀷
+        test_email_lang_type = "涓枃"
+        # hr鎶ュ憡閭欢璇█绫诲瀷
+        hr_report_email_lang_type = "涓枃"
+        # 鎶ュ憡妯℃澘
+        report_template = "MAQV2-Complete-Chinese"
+        # 娴嬭瘯鑰呮姤鍛婇偖浠惰瑷�绫诲瀷
+        member_report_email_lang_type = "涓枃"
+
+        # 濉啓娴嬭瘯鍖呭悕绉�
+        test_package_list.testPackageName_input(test_package_name)
+        # 濉啓鎶ュ憡浼佷笟鍚嶇О
+        test_package_list.reportCompanyName_input(report_company_name)
+        # 閫夋嫨娴嬭瘯鍖呯被鍨�
+        test_package_list.testPackageType_select(test_package_type)
+        # 閫夋嫨浜у搧鍖�
+        test_package_list.prodTree_select(prod_name)
+        # 杈撳叆hr閭
+        test_package_list.hrReportEmail_input(hr_email)
+        # 杈撳叆鏈夋晥鏈�
+        test_package_list.invalidTime_input(invalid_time)
+        # 閫夋嫨閭�璇烽偖浠惰瑷�绫诲瀷
+        test_package_list.testTemplateLangType_select(test_email_lang_type)
+        # 閫夋嫨HR閭欢璇█绫诲瀷
+        test_package_list.hrTemplateLangType_select(hr_report_email_lang_type)
+        # 閫夋嫨鎶ュ憡妯℃澘
+        test_package_list.reportTemplate_select(report_template)
+        # 鐐瑰嚮娴嬭瘯鑰呮帴鏀舵姤鍛�
+        test_package_list.autoSendReport_checkbox()
+        # 閫夋嫨娴嬭瘯鑰呮姤鍛婇偖绠辫瑷�绫诲瀷
+        test_package_list.memberReportLangType_select(member_report_email_lang_type)
+        # 鐐瑰嚮纭鎸夐挳
+        test_package_list.add_confirm()
+        time.sleep(2)
+        # 鍒ゆ柇鏄惁鎴愬姛鍒涘缓
+        assert test_package_list.is_create_success(test_package_name)
+
+    def test_open_url(self):
+        """
+        鎵撳紑鍒嗕韩閾炬帴
+        :return:
+        """
+        # 鑾峰彇driver
+        driver = TestSmoking.driver
+        # 鍒涘缓home椤甸潰鐨勫璞�
+        # home_page = HomePage(driver)
+        # 鐐瑰嚮宸︿晶鑿滃崟杩涘叆椤甸潰
+        # home_page.menu_select("娴嬭瘯鍖呯鐞�/娴嬭瘯鍖呭垪琛�")
+        # 鍒涘缓娴嬭瘯鍖呭垪琛ㄩ〉闈㈢殑瀵硅薄
+        test_package_list = TestPackageListPage(driver)
+        # 鍕鹃�夋柊鍒涘缓鐨勬祴璇曞寘
+        test_package_list.click_package_checkbox(TestSmoking.test_package_name)
+        # 鐐瑰嚮鐢熸垚鍒嗕韩閾炬帴鎸夐挳
+        test_package_list.click_share_1_btn()
+        # 鑾峰彇鍒皍rl
+        url = test_package_list.get_share_url()
+        # 鎵撳紑鏂版爣绛鹃〉鍔犺浇url
+        test_package_list.open_new_table_to_url(url)
+        # 鍒涘缓鍒嗕韩椤甸潰瀵硅薄
+        share_add = ShareAddPage(driver)
+        # 濉啓淇℃伅鎻愪氦骞惰幏鍙栨祴璇曢摼鎺�
+        name = random_string(8)
+        email = name + "@gmail.com"
+        test_url = share_add.page_default_operation(name, email)
+        # test_url = "http://161.117.57.194/exam-stu/#/ots/94830ada-8f6c-44c0-8946-79c43267d9c1/login"
+        # 褰撳墠椤甸潰鎵撳紑閾炬帴
+        driver.get(test_url)
+        # 鐐瑰嚮鎻愪氦鎸夐挳
+        share_add.submit_btn()
+        # 濉啓鍩烘湰淇℃伅骞舵彁浜�
+        share_add.fill_info(email, "鑱屽憳", "浜轰簨閮�")
+        # 鐐瑰嚮寮�濮嬫祴璇�
+        share_add.submit_btn()
+
+    def test_answer(self):
+        # 鑾峰彇driver
+        driver = TestSmoking.driver
+        # 鍒涘缓answer瀵硅薄
+        answer_page = AnswerPage(driver)
+        # 寮�濮嬬瓟棰�
+        answer_page.answer_MAQ()
+        # 绛斿畬棰樺叧闂綋鍓嶆爣绛鹃〉
+        driver.close()
+        # 鍒囨崲鍒扮涓�涓爣绛鹃〉
+        answer_page.switch_window(0)
+        # 鍒涘缓娴嬭瘯鍖呭垪琛ㄩ〉闈㈢殑瀵硅薄
+        test_package_list = TestPackageListPage(driver)
+        # 鍒犻櫎娴嬭瘯鍖�
+        test_package_list.delete_package()
+        time.sleep(5)
+        # 鍏抽棴
+        driver.quit()
+
+
+if __name__ == '__main__':
+    pytest.main(["-s", __file__])

--
Gitblit v1.9.1