栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

python(十二)运用Uiautomator2搭建UI自动化框架实战

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

python(十二)运用Uiautomator2搭建UI自动化框架实战

前言

由于公司UI自动化框架底层用的是Uiautomator2,所以我就用Uiautomator2搭了一套UI自动化框架,并运用某软件做了一个实战,思路其实和之前写的Appnium一样的,有时间我会上传到github

ps:最近一直很忙,之前的Appnium搭建的企业微信实战,有时间我再把功能实现 0.0

  • github链接:

  • Appnium-企业微信UI自动化框架搭建:https://blog.csdn.net/Makasa/article/details/123963798?spm=1001.2014.3001.5502


一、Uiautomator2介绍
  • 这篇文章写的很详细了大家可以学习学习,我就不再介绍了
    链接:https://ceshiren.com/t/topic/5396

二、整体框架介绍
base包:封装基础类	

case包:封装测试用例类

datas包:参数化数据(excel 或者 yml...)

logs包:封装日志

page包:封装所有的页面

sms包:发送邮箱

report包:存放测试报告

utils包:工具包,例如读取参数化数据


三、框架搭建 Base包: 1、先创建一个BasePage.py
  • 为什么要单独封装一个BasePage呢? 如果说以后我们不用uiautomator2这个框架了,我们只需要更改BasePage即可,不会影响到其他类的代码,从某种程度上来说也就是进行解耦

  • 另外,这个类也可以封装自己写的公用的方法,例如:重复性很高的代码,这些方法不论在哪个app里都能用的话,我们就单独拧出来封装成一个方法,例如下我写的:关闭弹窗/重新传值等

"""
    基类,大概率不变的东西放在基类
    例如,框架本身的东西,或者自己封装的公用方法
"""
from datetime import time
from time import sleep

import uiautomator2 as u2

device = 'JJN6R18305005632'


class BasePage:
    def __init__(self):
        self.d = u2.connect(device)

    def back(self, num):
        # 返回
        for i in range(num):
            self.d.xpath("//*[@resource-id='com.zhenai.loveguider:id/iv_back']").click()

    def clear_input_x_by_resourceId(self, location, value, x):
        """
            清空已填写的选项,重新传值
            :param location: 元素定位字符串
            :param value: 重新传的值
            :param x :索引
        """
        self.d(resourceId=location)[x].click()  # 定位到元素
        self.d(focused=True).clear_text()
        self.d.send_keys(value)

    def clear_input_by_resourceId(self, location, value):
        """
            清空已填写的选项,重新传值
            :param location: 元素定位字符串
            :param value: 重新传的值
        """
        self.d(resourceId=location).click()  # 定位到元素
        self.d(focused=True).clear_text()
        self.d.send_keys(value)
        return self

    def close_popup(self, text, locationType, location):
        """
            关闭弹窗
            :param text:字符串,弹窗包含的text文本
            :param locationType:定位方式 xpath or resourceId
            :param location:定位元素字符串,例:"//*[@text='登录']"
        """
        element = self.d(text=text)
        while True:
            if element:
                element.click()
                continue
            else:
                print("权限校验弹窗不存在")
                break
        if locationType == "xpath":
            self.d.xpath(location).click()
        elif locationType == "resourceId":
            self.d(resourceId=location).click()

    def swipe_until_element_found(self, param, wait_after_found=0.0, **kwargs):
        """
        检查元素是否存在,若不存在则进行上滑,滑动后再次检查,直到滑动到页面底部
        若找到元素则返回,否则滑动到页面底部后,仍未找到元素,则抛出异常,提示找不到元素
        :param param: xpath字符串 或 元素对象
        :param wait_after_found: 找到元素后,原地等待时间
        :param kwargs:
        :return:
        """
        element = self.d.xpath(param) if isinstance(param, str) else param
        param = param if isinstance(param, str) else param.selector
        while True:
            try:
                assert element.exists
                if wait_after_found:
                    print("Element found,sleep {} seconds".format(wait_after_found))
                sleep(wait_after_found)
                return element
            except AssertionError:
                print("Element 【 {} 】 Not found, Continue to swipe up...".format(param))
                # 获取滑动前页面下半部分的所有元素
                page_content = self.d.dump_hierarchy()[(len(self.d.dump_hierarchy()) // 2):]
                # self.up(**kwargs)
                self.d.swipe_ext("up")
                sleep(0.5)
                # 获取滑动后页面下半部分的所有元素,并与上一次滑动前的页面元素对比,页面元素没有变化时跳出循环
                if self.d.dump_hierarchy()[(len(self.d.dump_hierarchy()) // 2):] == page_content:
                    break
        if not element.exists:
            raise AssertionError("Element 【 {} 】 located failed in this page".format(param))

    def swipe_for_click(self, param, wait_after_click=0.0, **kwargs):
        """
        判断UI元素是否存在, 不存在则持续向上滑动到底部,直到UI元素在页面内出现,再进行点击
        :param param: xpath字符串 或 元素对象
        :param wait_after_click: 点击后等待时间
        :return:
        """
        element = self.swipe_until_element_found(param, **kwargs)
        element.click()
        if wait_after_click:
            print("Element found and click,then sleep {} seconds".format(wait_after_click))
        sleep(wait_after_click)

    def up(self, scale=0.9, times=1, duration=1.0, **kwargs):
        """
        上滑操作
        :param scale: 滑动单位,默认0.9个单位
        :param times: 滑动次数,默认1次
        :param duration: 滑动时间,默认1.0秒
        :return:
        """
        for i in range(times):
            self.d.swipe_ext("up", scale, duration=duration, **kwargs)

    def down(self, scale=0.9, times=1, duration=1.0, **kwargs):
        """
        下滑操作
        :param scale: 滑动单位,默认0.9个单位
        :param times: 滑动次数,默认1次
        :param duration: 滑动时间,默认1.0秒
        :return:
        """
        for i in range(times):
            self.d.swipe_ext("down", scale, duration=duration, **kwargs)

    def left(self, scale=0.9, times=1, duration=1.0, **kwargs):
        """
        左滑操作
        :param scale: 滑动单位,默认0.9个单位
        :param times: 滑动次数,默认1次
        :param duration: 滑动时间,默认1.0秒
        :return:
        """
        for i in range(times):
            self.d.swipe_ext("left", scale, duration=duration, **kwargs)

    def right(self, scale=0.9, times=1, duration=1.0, **kwargs):
        """
        右滑操作
        :param scale: 滑动单位,默认0.9个单位
        :param times: 滑动次数,默认1次
        :param duration: 滑动时间,默认1.0秒
        :return:
        """
        for i in range(times):
            self.d.swipe_ext("right", scale, duration=duration, **kwargs)

    def wait_until_element_found(self, param, timeout=30.0, retry_interval=2, wait_after_found=0.0):
        """
        定位元素,如果不存在就间隔若干秒后重试,直到元素定位成功或超时
        :param param: xpath字符串 或 元素对象
        :param timeout: 超时, 默认30秒
        :param retry_interval: 间隔时间, 默认2秒
        :param wait_after_found: 找到元素后,原地等待时间
        :return:
        """
        element = self.d.xpath(param) if isinstance(param, str) else param
        max_time = time() + timeout
        while True:
            try:
                assert element.exists
                if wait_after_found:
                    print("Element found,then sleep {} seconds".format(wait_after_found))
                sleep(wait_after_found)
                return element
            except AssertionError:
                param = param if isinstance(param, str) else param.selector
                print("Element 【 {} 】 Not found, Retry...".format(param))
                if time() > max_time > 0:
                    raise AssertionError("Element 【 {} 】 located failed after {} timeout".format(param, timeout))
                sleep(retry_interval)

    def wait_for_click(self, param, wait_after_click=0.0, **kwargs):
        """
        判断UI元素是否存在, 不存在则等待UI元素在一定时间内出现,再进行点击
        :param param: xpath字符串 或 元素对象
        :param wait_after_click: 点击后等待时间
        :return:
        """
        element = self.wait_until_element_found(param, **kwargs)
        element.click()
        if wait_after_click:
            print("Element found and click,then sleep {} seconds".format(wait_after_click))
        sleep(wait_after_click)

    def repeat_click(self, param, times, wait_after_repeat_click=0.0):
        """
        重复多次点击UI元素
        :param param: xpath字符串 或 元素对象
        :param times: 点击次数
        :param wait_after_repeat_click: 重复点击后等待时间,默认为0.0
        :return:
        """
        element = self.wait_until_element_found(param)
        for i in range(times):
            element.click()
        if wait_after_repeat_click:
            print("Element click,then sleep {} seconds".format(wait_after_repeat_click))
        sleep(wait_after_repeat_click)

2、创建一个LoverPadApp.py,继承BasePage
  • 由于以后可能又去测试其他的App,所以我们这边就单独再创建一个LoverPadApp的类继承BasePage,为了区分App
"""
    恋爱宝相关的操作
"""
from time import sleep

from mikasa_script.pad_ui_auto.base.BasePage import BasePage


class LoverPadApp(BasePage):
    def start(self):
        """
            启动App
        """
        if self.d == None:
            self.d.app_start('com.zhenai.loveguider')
        return self.d

    def restart(self):
        """
            重启App
        """
        pass

    def clear(self):
        """
            清除app缓存
        """
        self.d.app_clear('com.zhenai.loveguider')

    def stop(self):
        """
            关闭App
        """
        self.d.app_stop('com.zhenai.loveguider')

    def login_goto_main(self, workerId, password):
        """
            员工登录进首页
        """
        from mikasa_script.pad_ui_auto.page.MainPage import MainPage
        if self.d != None:
            return MainPage(self.d)
        # 入口
        location = 'com.zhenai.loveguider:id/edittext'
        self.clear_input_x_by_resourceId(location, workerId, 0)
        self.clear_input_x_by_resourceId(location, password, 1)
        sleep(1)
        self.d.xpath("//*[@text='立即登录']").click()
        sleep(2)
        return MainPage(self.d)


if __name__ == '__main__':
    LoverPadApp().clear()

Page包:
  • 这里封装企业微信所有要测试的页面
  • 思路:一个页面封装为一个类,页面上的每个功能封装为一个方法
  • 另外元素的定位方法,大家视情况而定:
    1)如果APP很多id是动态生成的话,就建议使用:Xpath定位,因为id可能不是唯一的,是动态生成的,换一台设备后,原代码可能就会因为找不到id而无法运行。
    为了提高代码设计的兼容性,我们尽量考虑到以后前端元素变更又或者测试设备更换的情况
    2)如果Id是唯一的,就建议用id查找,因为Xpath定位比id查找慢
1、创建首页,MainPage.py
"""
    恋爱宝首页
"""

from mikasa_script.pad_ui_auto.base.LoverPadApp import LoverPadApp
from mikasa_script.pad_ui_auto.page.MemberLoginPage import MemberLoginPage


class MainPage(LoverPadApp):
    # python 类型提示
    def __init__(self, d):
        # 拿到上个页面的driver
        self.d = d

    def click_member_login(self):
        """
            点击会员登录按钮
        """
        if self.d != None:
            return MemberLoginPage(self.d)
        # 如果有弹窗,点击始终允许
        self.close_popup("始终允许", None, None)
        location = "//*[@text='登录']"
        self.d.xpath(location).click()
        self.close_popup("始终允许", None, None)
        return MemberLoginPage(self.d)

2、创建会员登录页面,MemberLoginPage.py
"""
    恋爱宝会员登录页
"""
from time import sleep


from mikasa_script.pad_ui_auto.base.LoverPadApp import LoverPadApp
from mikasa_script.pad_ui_auto.page.MemberMainPage import MemberMainPage


class MemberLoginPage(LoverPadApp):
    # python 类型提示
    def __init__(self, d):
        # 拿到上个页面的driver
        self.d = d

    def member_login(self, memberId):
        """
            会员到店登录
        """
        # if self.d is not None:
        #     return MemberMainPage(self.d)
        self.d.xpath("//*[@text='请输入会员手机号或ID']").click()
        self.d.send_keys(memberId)
        self.d.xpath("//*[@text='请选择接待包间']").click()
        sleep(1)
        self.d.xpath("//*[@text='测试包间']").click()
        sleep(2)
        self.d(text="进入包间").click()
        sleep(2)
        return MemberMainPage(self.d)

3、创建会员页面,MemberMainPage.py
"""
    恋爱宝:会员界面
"""
from time import sleep

from mikasa_script.pad_ui_auto.base.LoverPadApp import LoverPadApp
from mikasa_script.pad_ui_auto.page.CommitLetterPage import CommitLetterPage
from mikasa_script.pad_ui_auto.page.EmoCounselingPage import EmoCounselingPage
from mikasa_script.pad_ui_auto.page.EmoTestPage import EmoTestPage
from mikasa_script.pad_ui_auto.page.HighVipPage import HighVipPage
from mikasa_script.pad_ui_auto.page.IdCardPage import IdCardPage
from mikasa_script.pad_ui_auto.page.ImgEvaluationPage import ImgEvaluationPage
from mikasa_script.pad_ui_auto.page.MemberInfoPage import MemberInfoPage
from mikasa_script.pad_ui_auto.page.MyOrderPage import MyOrderPage


class MemberMainPage(LoverPadApp):

    def __init__(self, d):
        # 拿到上个页面的driver
        self.d = d

    def check_room_exist(self):
        """
            校验包间名字是否存在
        """
        element = self.d(text="包间:测试包间")
        if element:
            return True
        else:
            return False

    def click_info_table_button(self):
        """
            点击信息表按钮
        """
        # if self.d is not None:
        #     return MemberInfoPage(self.d)
        self.d.xpath("//*[@text='信息表']").click()
        sleep(2)
        return MemberInfoPage(self.d)

    def click_emo_test_button(self):
        """
            点击情感测评按钮
        """
        self.d.xpath("//*[@text='情感测评']").click()
        sleep(2)
        return EmoTestPage(self.d)

    def click_img_evaluation_button(self):
        """
            点击形象评估按钮
        """
        self.d.xpath("//*[@text='形象评估']").click()
        sleep(2)
        return ImgEvaluationPage(self.d)

    def click_id_card_button(self):
        """
            点击身份证件按钮
        """
        self.d.xpath("//*[@text='身份证件']").click()
        sleep(2)
        return IdCardPage(self.d)

    def click_commit_letter_button(self):
        """
            点击承诺函按钮
        """
        self.d.xpath("//*[@text='承诺函']").click()
        sleep(2)
        return CommitLetterPage(self.d)

    def click_my_order_button(self):
        """
            点击我的订单按钮
        """
        self.d.xpath("//*[@text='我的订单']").click()
        sleep(2)
        return MyOrderPage(self.d)

    def click_high_vip_button(self):
        """
            点击高端VIP服务按钮
        """
        self.d.xpath("//*[@resource-id='com.zhenai.loveguider:id/service_rv']/android.widget.FrameLayout[1]").click()
        sleep(2)
        return HighVipPage(self.d)

    def click_emo_counseling_button(self):
        """
            点击情感咨询服务按钮
        """
        self.d.xpath("//*[@resource-id='com.zhenai.loveguider:id/service_rv']/android.widget.FrameLayout[2]").click()
        sleep(2)
        return EmoCounselingPage(self.d)

4、创建会员信息表页面,MemberInfoPage.py
"""
    恋爱宝:会员信息表界面
"""
from time import sleep

from mikasa_script.pad_ui_auto.base.LoverPadApp import LoverPadApp



class MemberInfoPage(LoverPadApp):
    def __init__(self, d):
        # 拿到上个页面的driver
        self.d = d

    def check_info_view(self):
        """
            校验信息填写后,是否是信息资料预览界面
        """
        self.d.xpath("//*[@text='信息表']").click()
        sleep(2)
        element = self.d(text="择偶标准")
        if element:
            return False
        else:
            return True

    def input_member_info(self):
        """
            填写会员信息
        """
        self.close_popup("我已知晓", None, None)
        sleep(0.5)
        if self.d != None:
            #  页面下滑,移动到有修改信息按钮,如果有则点击修改
            update_button = "//*[@resource-id='com.zhenai.loveguider:id/change']"
            self.swipe_for_click(update_button, wait_after_click=0.0)
            sleep(2)
            self.close_popup("我已知晓", None, None)

        next_step = "//*[@resource-id='com.zhenai.loveguider:id/btn_next']"
        self.swipe_for_click(next_step, wait_after_click=0.0)
        sleep(2)
        self.d.xpath("//*[@resource-id='com.zhenai.loveguider:id/bottom_btn_next']").click()  # 下一步
        sleep(2)
        # 填表人确认
        self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/commitment_agree1"]').click()  # 我承诺
        self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/bottom_btn_next"]').click()  # 提交并预览
        self.close_popup("继续保存", None, None)
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/tv_left"]').click()  # 不了,继续保存
        MemberInfoPage.back(self, 1)
        return MemberInfoPage(self.d)

        # 基本信息填写
        # self.clear_input_by_resourceId("//*[@resource-id='com.zhenai.loveguider:id/et_name']", "杨椰")
        # self.d(resourceId="com.zhenai.loveguider:id/rb_sex_female").click()
        # self.d(resourceId="com.zhenai.loveguider:id/select_info_tv").click()
        # self.d.xpath(
        #     "//*[@resource-id='com.zhenai.loveguider:id/select_ll_marriage']/android.widget.RelativeLayout[1]"
        #     "/android.widget.TextView[2]").click()  # 婚况
        # self.d(text="确定").click()
        # self.d.xpath("//*[@resource-id='com.zhenai.loveguider:id/select_ll_place']/android.widget.RelativeLayout[1]"
        #              "/android.widget.TextView[2]").click()  # 籍贯
        # self.d(text="确定").click()
        # self.d.xpath("//*[@resource-id='com.zhenai.loveguider:id/select_ll_census']/android.widget.RelativeLayout[1]/"
        #              "android.widget.TextView[2]").click()  # 户籍
        # self.d(text="确定").click()
        # self.d(resourceId="com.zhenai.loveguider:id/et_income").click()
        # self.d.send_keys("10000")  # 月收入
        # # TODO 屏幕滑动
        #
        # self.d.xpath('//*[@text="请选择"]').click()  # 职业
        # self.d(text="确定").click()
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/rg_house_no"]').click()  # 购房
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/no_buy_house_member_ll"]'
        #              '/android.widget.RelativeLayout[1]/android.widget.TextView[2]').click()  # 未购房详情
        # self.d(text="确定").click()
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/rg_car_no"]').click()  # 购车
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/select_ll_marry_time"]'
        #              '/android.widget.RelativeLayout[1]/android.widget.TextView[2]').click()  # 计划结婚时间
        # self.d(text="确定").click()
        # self.d.xpath(
        #     '//*[@resource-id="com.zhenai.loveguider:id/select_ll_comment_self"]/'
        #     'android.widget.RelativeLayout[1]/android.widget.TextView[2]').click()  # 自我评价
        # self.d.xpath(
        #     '//*[@resource-id="com.zhenai.loveguider:id/singleChoiceLv"]/'
        #     'android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.CheckBox[1]').click()  # 勾选
        # self.d(text="确定").click()
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/btn_next"]').click()
        # sleep(2)
        #
        # # 择偶标准
        # self.d.xpath(
        #     '//*[@resource-id="com.zhenai.loveguider:id/select_ll_height_range"]/android.widget.RelativeLayout[1]/android.widget.TextView[2]').click()  # 身高范围
        # self.d(text="确定").click()
        #
        # self.d.xpath(
        #     '//*[@resource-id="com.zhenai.loveguider:id/select_ll_marriage_requirement"]/android.widget.RelativeLayout[1]/android.widget.TextView[2]').click()  # 婚况要求
        # self.d(text="确定").click()
        #
        # self.d.xpath(
        #     '//*[@resource-id="com.zhenai.loveguider:id/select_ll_plage_range"]/'
        #     'android.widget.RelativeLayout[1]/android.widget.TextView[2]').click()  # 籍贯范围
        # self.d(text="确定").click()
        # self.d.xpath(
        #     '//*[@resource-id="com.zhenai.loveguider:id/select_ll_education_range"]/'
        #     'android.widget.RelativeLayout[1]/android.widget.TextView[2]').click()  # 学历范围
        # self.d.xpath(
        #     '//*[@resource-id="com.zhenai.loveguider:id/multiChoiceLv"]/android.widget.LinearLayout[5]/'
        #     'android.widget.FrameLayout[1]/android.widget.CheckBox[1]').click()  # 勾选本科
        #
        # self.d(text="确定").click()
        # self.d.xpath(
        #     '//*[@resource-id="com.zhenai.loveguider:id/select_ll_income_range"]/'
        #     'android.widget.RelativeLayout[1]/android.widget.TextView[2]').click()  # 月收入范围
        # self.d(text="确定").click()
        # self.d.xpath(
        #     '//*[@resource-id="com.zhenai.loveguider:id/select_ll_care"]/'
        #     'android.widget.RelativeLayout[1]/android.widget.TextView[2]').click()  # 最在意项
        # self.d.xpath(
        #     '//*[@resource-id="com.zhenai.loveguider:id/multiChoiceLv"]/'
        #     'android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.CheckBox[1]').click()  # 最在意项
        # self.d(text="确定").click()
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/et_jihui"]').click()  # 勾选
        # self.d.send_keys("暂无忌讳项")
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/bottom_btn_next"]"]').click()  # 下一步
        #
        # # 填表人确认
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/rb_self_yes"]').click()  # 勾选是本人
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/commitment_agree1"]').click()  # 我承诺
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/bottom_btn_next"]').click()  # 提交并预览
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/tv_left"]').click()  # 不了,继续保存

        # 点击返回,返回上一页
        # self.d.xpath('//*[@resource-id="com.zhenai.loveguider:id/iv_back"]').click()

case包:
  • 封装测试用例类
test_lover_pad.py
from mikasa_script.pad_ui_auto.base.LoverPadApp import LoverPadApp
from mikasa_script.pad_ui_auto.page.MemberMainPage import MemberMainPage
from mikasa_script.pad_ui_auto.utils.crm_api import *

workerId = "2046737"
pad_pwd = "yy1998123"
memberId = "1443736269"
crm_pwd = "AC2211AA1C44AFD3D08F3F09DFBF77CB"
phone = "199****7590"
verifyCode = "222222"


class TestLoverPad:
    def setup_class(self):
        # 用例开始之前,先确认用户进行到店
        sid = get_crm_sid(crm_pwd, phone, workerId, verifyCode)
        if not check_member_enter_shop(memberId, workerId, sid):
            # 若会员不存在到店记录,则进行到店操作
            sure_to_shop(workerId, memberId, sid)

    def setup(self):
        # 每个用例开始之前,启动恋爱宝
        self.app = LoverPadApp()
        pass

    def teardown(self):
        # 每个用例执行完成后,就默认返回到首页
        # self.app.clear()
        pass
        # self.app.back()

    def teardown_class(self):
        # 所有用例都结束后,则关闭企业微信
        # self.app.stop()
        pass

    # 用例一、会员登录
    def test_member_login(self):
        result = LoverPadApp().start().login_goto_main(workerId, pad_pwd).click_member_login().member_login(
            memberId).check_room_exist()
        # 断言包间名称是否存在
        assert True == result

    # 用例二、测试信息表界面
    def test_info_table(self):
        result = MemberMainPage(LoverPadApp().start()).click_info_table_button().input_member_info().check_info_view()
        assert True == result

    # 用例三、测试情感测评界面
    def test_emo_test(self):
        MemberMainPage(LoverPadApp().start()).click_emo_test_button().write_emo_test()
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/829040.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号