7 Star 44 Fork 14

TommyLemon / UIGO

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

UIGO

📱 零代码快准稳 UI 智能录制回放平台 🚀

像素级自动兼容任意宽高比分辨率屏幕,毫秒级自动精准等待网络请求,录制回放快、准、稳!

录制回放 快速上手 在线工具



UIGO - 📱 零代码快准稳 UI 智能录制回放平台 🚀

像素级自动兼容任意宽高比分辨率屏幕,毫秒级自动精准等待网络请求,录制回放快、准、稳!
适用于 一次录制到处回放、反复回归界面操作、App UI/功能 自动化测试、
帮助开发快速复现和排查 bug、方便判断 bug 原因出在前端还是后端 等,
大量减少耗时费力又无聊的重复手工操作,大幅提高手工和自动化测试效率,
强力杜绝 测试和开发、前端和后端 关于缺陷单踢皮球等各种低效扯皮内耗!

用户包含腾讯,应微信团队邀请分享了 零代码测试工具与实践(API•单元•UI)

支持功能

  • 零代码 录制和回放 触屏、按键、键盘、数据 等
  • 支持 16:9 标准屏、19.5:9 全面屏等各种设备屏幕
  • 支持原生页面、内置 H5 网页、浏览器加载网页等
  • 支持 Android 真机、Studio/Genymotion 等模拟器
  • 单双指点击、长按、滑动、缩放各种像素级精细操作
  • 自动精准等待、模拟 HTTP API 的请求和响应数据
  • 不同机型录制回放偏差基本仅在 3 像素、2 毫秒 内
  • 可从任意界面开始和停止录制、回放,绕过登录问题
  • 可自动对关键步骤截屏,方便对比回放与录制差异
  • 可自动和手动选择 View 及触摸区域、贴靠方式等
  • 可保存录制步骤相关数据到后端数据库及从后端下载
  • 可用管理端网页浏览检索用例和远程控制录制回放
  • 附带 UnitAuto-机器学习零代码自动化单元测试
  • 中文和英语双语文案,根据系统语言设置自动切换

特点优势

相比各种 UI 录制回放/自动化测试 的 其它平台/工具/框架:

1.它们录制过程各种别扭难用反人类,甚至还需要开发/维护用例脚本、每个用例都写一大堆代码频繁部署等;
UIGO 不需要写任何代码,录制几乎是按和人正常操作完全一样的方式,操作简单易用,录制回放快、准、稳!

2.它们很难兼容各种不同宽高比分辨率屏幕,720P, 1080P 等 16:9 屏幕录制最多只能较好地在 16:9 屏幕回放,
即便手写代码或图像比对等也很难在列表项 View id/图标 重复控件精准定位,经常点错位置导致大量回放失败;
UIGO 则能很好地支持 16:9, 19.5:9 等各种不同屏幕录制,然后在 720P, 1080P, 2K, 1080X2340, 1440X3200
等各种 不同机型、不同系统、不同屏幕 基本都能很好地精准回放,偏差基本仅在相当于一根头发丝的 3 像素内!


3.它们要到处人为设置/调整操作步骤等待时间,还总是要么等太久、要么还没返回就过早执行下一步导致出错,
因为几乎无法保证网络请求在精准时间内返回,所以总是界面没加载完就滑动、弹窗没显示就点了"确定"位置等;
UIGO 则会自动精准等待 App 发送的各种 HTTP API 网络请求,偏差基本在 2 毫秒内,比眨眼一次还要快 50 倍,
像专业的测试工程师一样精准高效地等待数据和 UI 都加载好并执行 点击、长按、滑动、缩放 等每一步对应操作!


原理说明

被测项目不需要写任何用例脚本代码(逻辑代码、注解代码、配置代码等全都不要),
UIGO 会自动录制 UI 触屏操作、虚拟+实体按键操作、HTTP API 网络请求与响应、
Activity, Fragment, Dialog, PopupWindow 等各种组件(控件)元素的生命周期 等,
回放时根据录制触摸点所在被分割球划分的 上、下、左、右、居中、等比 等区域
以及 屏幕分辨率、状态栏高度、导航栏高度、键盘高度 等来自动计算出回放触摸点,
再加上 id(如果有) 相同且距离最近的 View 区域来辅助微调,高度精准回放触屏操作!
对 返回按键、键盘按键 甚至 输入框编辑过程的每个变化的字符 也都能精准无误地还原!


示例项目

UIGO Android 简单测试 App 直接 下载 (第一次可能失败,返回报错 JSON,一般重试一次就可以)
UIGO Android 复杂客户端 App 直接 下载 (第一次可能失败,返回报错 JSON,一般重试一次就可以)

安装 App 必须授权 显示悬浮窗、读写文件存储 这两个权限

其它申请的权限也尽可能都勾选授权,如果不能提前授权,则在使用时弹出是否申请权限弹窗后再确认授权
https://github.com/TommyLemon/APIAuto/issues/61#issuecomment-1997047600


早期零代码单机录制不同分辨率双机同时回放视频

https://www.bilibili.com/video/BV1CK4218788

早期管理端网页工具零代码远程控制手机录制回放视频

https://www.bilibili.com/video/BV1wA4m137ha

早期仿微信朋友圈复杂 App 录制回放,弹窗、输入、网页、滑动、点击等

https://www.bilibili.com/video/BV1fH4y1E7gD

零代码录制回放 H5 移动端网页输入、滑动、点击等操作

https://www.bilibili.com/video/BV1TK421C7y4

录制用例

1.按业务 App 提供的方式打开 UIAuto 管理首页,例如 APIJSONApp 是登录后点击首页标题,UIAuto-Android 是点击首页 [自动 UI 测试] 按钮

2.点击 Record 录制按钮 > 点击顶部悬浮长条中间的 Record 录制 按钮开始录制 > 正常操作 App > 完成一个用例过程后,点击半透明圆形 〇 悬浮球完成录制

3.(可选)点击右下角 post 按钮上传录制的操作和数据等到后端数据库,可先编辑底部的后端服务器 HTTP URL Host 地址为你自己部署的 APIJSON 后端服务

录制注意事项

1.按贴靠方式调整分割球位置及贴靠方式
按下(MotionEvent.ACTION_DOWN 事件)对应 View 的屏幕 [X, Y] 坐标位置前,必须先完成这步(如果已经配置正确则跳过)

分割球对应横纵两条分割线把屏幕分成了 左上、右上、左下、右下 4 个区域,对应数学上按坐标轴划分的 第 二、一、三、四 象限;
点击顶部控制条左上角 # 按钮可切换单双分割球,相比单分割球可更精细地设置高级属性

双分割球中间会出现一个长方形触控区域,默认点击位置按居中处理,可点击切换 center 居中, ratio 等比, top 靠上, bottom 靠下, left 靠左, right 靠右;
分割球本身贴靠方式可以点击分割球来切换 top_left 左上, top_right 右上, bottom_left 左下, bottom_right 右下, ratio 等比,
ratio_top 靠上并垂直纵向等比, ratio_bottom 靠下并垂直纵向等比, ratio_left 靠左并水平横向等比, ratio_right 靠右并水平横向等比

2.滑动要该快则快、该慢则慢
分页列表/网格等滑动尽量快速到底,保证不同宽高比分辨率屏幕上都能在同侧(↓ 从上往下、↑ 从下往上、→ 从左往右、← 从右往左)上显示一致;
点按内部 View 前可缓慢滑动保证目标出现在屏幕内,且手离开屏幕前先在同一位置稳住别动,保证不继续自动滚动(不同机型/系统对惯性滚动处理不一致导致偏差)

3.触控的 View 尽量都有 id,且尽量不要改动
UIGO 会对有 id 的被触控 View 在回放时进行微调触控位置(不改变 UI 布局,仅微调触屏输入坐标位置),灵活适配不同屏幕尤其是不同品牌机型的 UI 差异

4.最好开启托管服务器代理来录制 HTTP API 接口请求与响应流量,用于回放操作时同时回放数据
保证回放到某个步骤和录制到同一步骤时布局一致,不会触控点按错位

5.不要跳出 App,例如跳转外部浏览器或微信、支付宝等其它 App 来上网、登录、分享、支付等
目前不支持跨 App 录制回放,当前 App 外的对应事件录制不到也回放不了,打算后续支持对接支持跨 App 录制回放/自动化测试 的项目,
目前碰到这个问题要么避开,要么等待或手动点 》按钮跳过等待来直接继续执行下一步


回放用例

1.参考 录制用例 1 来打开 UIAuto 管理首页 > 点击左下角 Remote 共享列表 按钮 > 点击打开其中一个和 App 及账号对应的用例,或者 录制用例 后直接进入用例详情界面

2.点击用例详情界面左下角 Replay 回放按钮,返回到用例对应开始录制的业务 App 界面,保证状态一致(都是刚进入后没有操作的界面,或者分页列表都下拉刷新过等)

3.点击顶部悬浮长条中间的 Replay 回放 按钮开始回放,观察每步操作前后,App 的 UI 展示、界面数据、界面跳转、弹窗显示、键盘输入等是否符合预期(和录制时表现一样)

回放注意事项

1.回放开始时 App 初始状态必须和录制时一致
包括所在页面(最好都在首页)、列表/网格滚动距离(最好都是没滚动的初始值 0)、中英文等语言设置、字体大小和样式设置、缓存/调试等自定义配置 等

2.系统的中英文等语言、字体大小和样式 等设置尽可能保持一致

3.最好开启托管服务器代理来回放录制时的 HTTP API 接口请求与响应流量数据


快速上手

可先跳过这个步骤,先下载体验 App 安装包,安装后 按以下 录制用例、回放用例 文档来操作

集成到被测项目 Android 客户端 App

1.依赖 UnitAuto-Apk

UnitAuto-Apk 导入到你项目 app moudule 所在目录settings.gradle

include ':UnitAuto-Apk'

app moudule 目录build.gradle

dependencies {
    api project(':UnitAuto-Apk')
}

2.依赖 UIAuto

UIAuto 导入到你项目 app moudule 所在目录settings.gradle

include ':UIAuto'

app moudule 目录build.gradle

dependencies {
    api project(':UIAuto')
}

2.初始化 UIAuto

Application onCreate 方法 中初始化

    @Override
    public void onCreate() {
        super.onCreate();
        UIAutoApp.STEP_TIMEOUT = 30*1000; // 一般为 HTTP API 网络请求超时时间 ms 毫秒值
        UIAutoApp.getInstance().initUIAuto(this);
    }

3.提供 UIAuto 管理界面入口

AndroidManifest.xml 中注册 UIAutoActivity

<manifest ... >
    <application ... >
      
        <activity
            android:name="uiauto.UIAutoActivity"
            android:label="@string/ui"
            android:windowSoftInputMode="adjustPan"
            android:configChanges="orientation|screenSize"
            android:screenOrientation="portrait"
            />
        <activity
            android:name="uiauto.UIAutoListActivity"
            android:label="@string/ui"
            android:windowSoftInputMode="adjustPan"
            android:screenOrientation="portrait"
            />

        <activity
            android:name="unitauto.apk.UnitAutoActivity"
            android:label="@string/unit"
            android:windowSoftInputMode="adjustPan"
            android:configChanges="orientation|screenSize"
            android:screenOrientation="userLandscape"
            />
      
     </application>
</manifest>

可在你项目的任何界面新增一个按钮或其它形式的入口,仅 DEBUG 模式下展示

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClickUI"
        android:text="UIAutoActivity"
        android:textAllCaps="false"
        />

参考 layout/main_tab_activity

点击这个入口跳转到 UIAutoActivity

    public void onClickUI(View v) {
        startActivity(UIAutoActivity.createIntent(this));
    }

参考 MainTabActivity

4.通知 HTTP API 请求与相应

在 HTTP API 发起请求处加上

		UIAutoApp.getInstance().post(new Runnable() {
			@Override
			public void run() {
				UIAutoApp.getInstance().onHTTPEvent(
					action, type, method, host, url, headerStr, reqBodyStr, null, activity, fragment
				);
			}
		});

参考:
https://github.com/TommyLemon/UIGO/blob/master/APIJSONApp/app/src/main/java/apijson/demo/manager/HttpManager.java#L137-L147

在 HTTP API 响应结果处加上

		UIAutoApp.getInstance().post(new Runnable() {
			@Override
			public void run() {
				UIAutoApp.getInstance().onHTTPEvent(
					- action, responseCode, method, host, url, headerStr, reqBodyStr, resBodyStr, null, activity, fragment
				);
			}
		});

参考:
https://github.com/TommyLemon/UIGO/blob/master/APIJSONApp/app/src/main/java/apijson/demo/manager/HttpManager.java#L168-L179


5.通知 Dialog, PopupWindow 显示和隐藏

业务代码中如果使用了 android.app.AlertDialog,且录制回放用例涉及,则可以换成 uiauto.AlertDialog,
最简单的方式是顶部菜单 Edit > Find > Replace in files 全局搜索 import android.app.AlertDialog,
然后点搜索弹窗右下角 Replace All 按钮批量改为 import uiauto.AlertDialog。
如果因为用了自定义或第三方通用 Base Alert Dialog 不方便替换,则可以 extends uiauto.AlertDialog 或在里面加上:

	@Override
	public void show() {
		super.show();
		UIAutoApp.getInstance().onUIAutoDialogShow(this); // 通知已显示
	}

	// 拦截隐藏事件监听
	private OnDismissListener listener;
	@Override
	public void setOnDismissListener(OnDismissListener listener) {
		this.listener = listener;
	}

	private Activity context;
	private void init(Context ctx) {
		this.context = (Activity) ctx;

		super.setOnDismissListener(new OnDismissListener() {
			@Override
			public void onDismiss(DialogInterface dialog) {
				if (listener != null) {
					listener.onDismiss(dialog);
				}

				UIAutoApp.getInstance().onUIAutoDialogDismiss(Dialog.this); // 通知已隐藏
			}
		});
	}

	// 每个可重写的构造方法都保证调用到 init 方法
	public AlertDialog(Context context) {
		super(context);
		init(context);
	}

	public AlertDialog(Context context, @StyleRes int themeResId) {
		super(context, themeResId);
		init(context);
	}

	public AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
		super(context, cancelable, cancelListener);
		init(context);
	}

所有 Dialog, DatePickerDialog, TimePickerDialog, ProgressDialog, CharacterPickerDialog 等也同上处理。


业务代码中如果使用了 android.widget.PopupWindow,且录制回放用例涉及,则可以换成 uiauto.PopupWindow,
最简单的方式是顶部菜单 Edit > Find > Replace in files 全局搜索 import android.widget.PopupWindow,
然后点搜索弹窗右下角 Replace All 按钮批量改为 import uiauto.PopupWindow。
如果因为用了自定义或第三方通用 Base Popup Window 不方便替换,则可以 extends uiauto.PopupWindow 或在里面加上:

    private android.widget.PopupWindow popupWindow;
    private View view;

    @Override
    public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
        super.showAsDropDown(anchor, xoff, yoff, gravity);

        if (view == null) {
            try {
                Field field = android.widget.ListPopupWindow.class.getDeclaredField("mPopup");
                field.setAccessible(true);
                popupWindow = (android.widget.PopupWindow) field.get(this);
                //  popupWindow.setOutsideTouchable(false);

                Field dvField = android.widget.PopupWindow.class.getDeclaredField("mDecorView");
                dvField.setAccessible(true);
                view = (View) dvField.get(popupWindow);

                if (view == null) {
                    View cv = popupWindow.getContentView();
                    view = cv == null ? null : cv.getRootView();
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

        if (view == null) {
            View cv = getContentView();
            view = cv == null ? null : cv.getRootView();
        }

        Window w = getWindow();

        // 通知已显示
        UIAutoApp app = UIAutoApp.getInstance();
        app.onUIAutoWindowCreate(w.getCallback(), w);
        app.setCurrentPopupWindow(popupWindow, view, null, context, null);
    }


    // 拦截隐藏事件监听
    private android.widget.PopupWindow.OnDismissListener listener;
    @Override
    public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener listener) {
        this.listener = listener;
    }

    public Window getWindow() {
        Activity ctx = view == null ? null : (Activity) view.getContext();
        if (ctx == null) {
            ctx = context;
        }
        return ctx.getWindow();
    }

    private Activity context;
    private void init(Context ctx) {
        this.context = (Activity) ctx;

        super.setOnDismissListener(new android.widget.PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                if (listener != null) {
                    listener.onDismiss();
                }

                Window w = getWindow();

                // 通知已隐藏
                UIAutoApp app = UIAutoApp.getInstance();
                app.onUIAutoWindowDestroy(w.getCallback(), w);
                app.setCurrentPopupWindow(null, null, null, context, null);
            }
        });
    }

    // 每个可重写的构造方法都保证调用到 init 方法
    public PopupWindow(Context context) {
        super(context);
        init(context);
    }

    public PopupWindow(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public PopupWindow(Context context, AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public PopupWindow(Context context, AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    public PopupWindow(Context context, View contentView) {
        super(contentView);
        init(context);
    }

    public PopupWindow(Context context, int width, int height) {
        super(width, height);
        init(context);
    }

    public PopupWindow(Context context, View contentView, int width, int height) {
        super(contentView, width, height);
        init(context);
    }

    public PopupWindow(Context context, View contentView, int width, int height, boolean focusable) {
        super(contentView, width, height, focusable);
        init(context);
    }

所有 ListPopupWindow 等也同上处理。

Java 后端 Server

可先跳过,使用 http://apijson.cn:8080http://apijson.cn:9090 代替

见 APIJSON-Demo 后端上手
https://github.com/APIJSON/APIJSON-Demo?tab=readme-ov-file#1%E5%90%8E%E7%AB%AF%E4%B8%8A%E6%89%8B


录制、回放用例

见以上 录制用例回放用例 的说明。



常见问题

1.apijson.cn 访问不了

托管服务地址改为 http://47.98.196.224:8080
https://github.com/TommyLemon/APIAuto/issues/13


更多常见问题
https://github.com/TommyLemon/APIAuto/issues


技术交流

关于作者

https://github.com/TommyLemon

如果有什么问题或建议可以 去 APIAuto 提 issue,交流技术,分享经验。
如果你解决了某些 bug,或者新增了一些功能,欢迎 提 PR 贡献代码,感激不尽。

其它项目

APIJSON 🚀 腾讯零代码、全功能、强安全 ORM 库 🏆 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构

APIAuto 敏捷开发最强大易用的 HTTP 接口工具,机器学习零代码测试、生成代码与静态检查、生成文档与光标悬浮注释,集 文档、测试、Mock、调试、管理 于一体的一站式体验

UnitAuto 机器学习零代码单元测试平台,零代码、全方位、自动化 测试 方法/函数 的正确性、可用性和性能

SQLAuto 智能零代码自动化测试 SQL 语句执行结果的数据库工具,任意增删改查、任意 SQL 模板变量、一键批量生成参数组合、快速构造大量测试数据

Android-ZBLibrary Android MVP 快速开发框架,Demo 全面,注释详细,使用简单,代码严谨

持续更新

https://github.com/TommyLemon/UIGO/commits/master

我要赞赏

UIGO 从私有仓库默默开发了 3 年多到现在终于开源了,Apache 证书对商用和非商用都很友好。
创作不易、坚持更难,右上角点亮 ⭐ Star 支持/收藏下本项目吧,谢谢 ^_^
https://github.com/TommyLemon/UIGO

Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright ©2020 TommyLemon(https://github.com/TommyLemon/UIAuto) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

📱 零代码快准稳 UI 智能录制回放平台 🚀 像素级自动兼容任意宽高比分辨率屏幕,毫秒级自动精准等待网络请求,录制回放快、准、稳!用户包含腾讯,应微信团队邀请分享了 零代码测试工具与实践(API•单元•UI) 展开 收起
Apache-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/TommyLemon/UIGO.git
git@gitee.com:TommyLemon/UIGO.git
TommyLemon
UIGO
UIGO
master

搜索帮助