阳洁
2023-07-12 cc5f08a16a061f2c1fbbbfcc5e27efa5aa003e76
测评系统自动测试
21个文件已添加
1505 ■■■■■ 已修改文件
base/__init__.py 补丁 | 查看 | 原始文档 | blame | 历史
base/base_page.py 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
comm/__init__.py 补丁 | 查看 | 原始文档 | blame | 历史
comm/my_random.py 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
elements/__init__.py 补丁 | 查看 | 原始文档 | blame | 历史
elements/answer_elements.py 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
elements/home_elements.py 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
elements/login_elements.py 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
elements/share_add_elements.py 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
elements/test_package_list_elements.py 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.py 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
po/__init__.py 补丁 | 查看 | 原始文档 | blame | 历史
po/answer_page.py 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
po/home_page.py 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
po/login_page.py 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
po/share_add_page.py 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
po/test_package_list_page.py 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
report/assets/style.css 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
report/report.html 305 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
testcase/__init__.py 补丁 | 查看 | 原始文档 | blame | 历史
testcase/test_smoking.py 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
base/__init__.py
base/base_page.py
New file
@@ -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)
comm/__init__.py
comm/my_random.py
New file
@@ -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
elements/__init__.py
elements/answer_elements.py
New file
@@ -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")
elements/home_elements.py
New file
@@ -0,0 +1,8 @@
from selenium.webdriver.common.by import By
class HomeElements:
    # 侧边栏菜单
    menu_ul = (By.XPATH, "//ul[@id='side-menu']")
elements/login_elements.py
New file
@@ -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")
elements/share_add_elements.py
New file
@@ -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")
elements/test_package_list_elements.py
New file
@@ -0,0 +1,75 @@
from selenium.webdriver.common.by import By
class TestPackageListElements:
    # 测试包列表iframe
    this_iframe = (By.XPATH, "//iframe[@src='/exam/test_package']")
    # 添加测试包iframe
    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]")
main.py
New file
@@ -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()
po/__init__.py
po/answer_page.py
New file
@@ -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
po/home_page.py
New file
@@ -0,0 +1,52 @@
from base.base_page import BasePage
from selenium.webdriver.common.by import By
import time
class HomePage(BasePage):
    """
    主页面侧边栏菜单与tab栏操作页面
    """
    def menu_select(self, menu_text, is_first=True):
        """
        选择菜单
        :param menu_text: 菜单选项内容,使用 / 分隔上下级菜单
        :param is_first: 是否为一级菜单,默认为True
        :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)
po/login_page.py
New file
@@ -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
po/share_add_page.py
New file
@@ -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)
po/test_package_list_page.py
New file
@@ -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):
        """初始化时将iframe切换到当前页面"""
        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)
        # 切换到add窗口的iframe
        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)
        # 切换到prodTree的iframe
        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:
        """
        # 回到上一层iframe
        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:
        """
        # 获取到table的所有tr
        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:
                # 复选框在tr中的定位
                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
        """
        # 切换到share的iframe中
        self.switch_iframe(PackageList.share_iframe)
        # 获取url
        url = self.get_input_value_js(PackageList.share_url_input)
        # 退回上一层iframe
        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
report/assets/style.css
New file
@@ -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;
}
report/report.html
New file
@@ -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>
testcase/__init__.py
testcase/test_smoking.py
New file
@@ -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()
        # 获取到url
        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__])