| <!DOCTYPE html> |
| <html lang="zh-CN"> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width,initial-scale=1"> |
| <title>背景介绍 | WEEX</title> |
| <meta name="description" content="Weex"> |
| <link rel="icon" href="//gw.alicdn.com/tps/TB1XNqxPXXXXXcSXVXXXXXXXXXX-64-63.png"> |
| <script src="//g.alicdn.com/alilog/mlog/aplus_v2.js"></script> |
| <meta name="data-spm" dataSpmProtocol="i" content="a2c7j"> |
| <meta name="aplus-ajax" content="chksum"> |
| <meta name="aplus-waiting" content="MAN"> |
| <meta name="google-site-verification" content="FbH8DPHpxdDJlfkKLKXuXWOu69DI8ZRRP8O2Phg8UKw"> |
| <meta name="baidu-site-verification" content="WRr1iWvsYK"> |
| |
| <link rel="preload" href="/assets/css/styles.0ff943f0.css" as="style"><link rel="preload" href="/assets/js/app.0ff943f0.js" as="script"><link rel="preload" href="/assets/js/115.d7e38c3b.js" as="script"><link rel="prefetch" href="/assets/css/1.styles.aa72e838.css"><link rel="prefetch" href="/assets/css/10.styles.1fff5b86.css"><link rel="prefetch" href="/assets/css/11.styles.a03faa50.css"><link rel="prefetch" href="/assets/css/2.styles.fc5d9892.css"><link rel="prefetch" href="/assets/css/21.styles.9ebad2b1.css"><link rel="prefetch" href="/assets/css/3.styles.6b94323a.css"><link rel="prefetch" href="/assets/css/36.styles.6dbb9d8c.css"><link rel="prefetch" href="/assets/css/4.styles.706a5607.css"><link rel="prefetch" href="/assets/css/7.styles.469d3b64.css"><link rel="prefetch" href="/assets/css/8.styles.b36167bf.css"><link rel="prefetch" href="/assets/css/9.styles.5bed6ebe.css"><link rel="prefetch" href="/assets/js/10.1fff5b86.js"><link rel="prefetch" href="/assets/js/100.09076a87.js"><link rel="prefetch" href="/assets/js/101.71fa3538.js"><link rel="prefetch" href="/assets/js/102.8a9926d6.js"><link rel="prefetch" href="/assets/js/103.d6c3649e.js"><link rel="prefetch" href="/assets/js/104.adb2425b.js"><link rel="prefetch" href="/assets/js/105.a063ca34.js"><link rel="prefetch" href="/assets/js/106.620e227f.js"><link rel="prefetch" href="/assets/js/107.1c97d156.js"><link rel="prefetch" href="/assets/js/108.d1f236a9.js"><link rel="prefetch" href="/assets/js/109.10a909cc.js"><link rel="prefetch" href="/assets/js/11.a03faa50.js"><link rel="prefetch" href="/assets/js/110.6f9200c8.js"><link rel="prefetch" href="/assets/js/111.9c1831b3.js"><link rel="prefetch" href="/assets/js/112.e5c0d546.js"><link rel="prefetch" href="/assets/js/113.b1c17feb.js"><link rel="prefetch" href="/assets/js/114.c24b95ed.js"><link rel="prefetch" href="/assets/js/116.1aaa95ab.js"><link rel="prefetch" href="/assets/js/117.1a9e5539.js"><link rel="prefetch" href="/assets/js/118.8d915477.js"><link rel="prefetch" href="/assets/js/119.77f0a126.js"><link rel="prefetch" href="/assets/js/12.16a9b730.js"><link rel="prefetch" href="/assets/js/120.3cf0ba10.js"><link rel="prefetch" href="/assets/js/121.47fefbf1.js"><link rel="prefetch" href="/assets/js/122.3dd022e8.js"><link rel="prefetch" href="/assets/js/123.84abc63f.js"><link rel="prefetch" href="/assets/js/124.2568e233.js"><link rel="prefetch" href="/assets/js/125.5b665f24.js"><link rel="prefetch" href="/assets/js/126.25b58a0a.js"><link rel="prefetch" href="/assets/js/127.d62a58c6.js"><link rel="prefetch" href="/assets/js/128.e5774a02.js"><link rel="prefetch" href="/assets/js/129.9a813c06.js"><link rel="prefetch" href="/assets/js/13.f8bde866.js"><link rel="prefetch" href="/assets/js/130.edddc316.js"><link rel="prefetch" href="/assets/js/131.760aa65c.js"><link rel="prefetch" href="/assets/js/132.559d5ba7.js"><link rel="prefetch" href="/assets/js/133.cbdd35c7.js"><link rel="prefetch" href="/assets/js/134.c02d92e1.js"><link rel="prefetch" href="/assets/js/135.e6d5496a.js"><link rel="prefetch" href="/assets/js/136.8826af60.js"><link rel="prefetch" href="/assets/js/137.28b4573d.js"><link rel="prefetch" href="/assets/js/138.d370fc15.js"><link rel="prefetch" href="/assets/js/139.8d9150fc.js"><link rel="prefetch" href="/assets/js/14.a98602cc.js"><link rel="prefetch" href="/assets/js/140.f0a9b5e1.js"><link rel="prefetch" href="/assets/js/141.297fa6fe.js"><link rel="prefetch" href="/assets/js/142.82bbcbaa.js"><link rel="prefetch" href="/assets/js/143.2d1ed3b2.js"><link rel="prefetch" href="/assets/js/144.69a9bc6a.js"><link rel="prefetch" href="/assets/js/145.74d52c12.js"><link rel="prefetch" href="/assets/js/146.12ecdaf4.js"><link rel="prefetch" href="/assets/js/147.daad4b96.js"><link rel="prefetch" href="/assets/js/148.bb356182.js"><link rel="prefetch" href="/assets/js/149.56fdaace.js"><link rel="prefetch" href="/assets/js/15.ea6e5024.js"><link rel="prefetch" href="/assets/js/150.ee9c4994.js"><link rel="prefetch" href="/assets/js/151.2feae618.js"><link rel="prefetch" href="/assets/js/152.c7703521.js"><link rel="prefetch" href="/assets/js/153.3b555eab.js"><link rel="prefetch" href="/assets/js/154.c6329253.js"><link rel="prefetch" href="/assets/js/155.334c4ea4.js"><link rel="prefetch" href="/assets/js/156.1a29657f.js"><link rel="prefetch" href="/assets/js/157.ba8f07bc.js"><link rel="prefetch" href="/assets/js/158.a60a60ba.js"><link rel="prefetch" href="/assets/js/159.9ce96d49.js"><link rel="prefetch" href="/assets/js/16.14acc638.js"><link rel="prefetch" href="/assets/js/160.2a970224.js"><link rel="prefetch" href="/assets/js/161.a26e2b53.js"><link rel="prefetch" href="/assets/js/162.73535662.js"><link rel="prefetch" href="/assets/js/163.7518a0a5.js"><link rel="prefetch" href="/assets/js/164.17367778.js"><link rel="prefetch" href="/assets/js/165.46c8f055.js"><link rel="prefetch" href="/assets/js/166.1cc22037.js"><link rel="prefetch" href="/assets/js/167.3fbcb5c6.js"><link rel="prefetch" href="/assets/js/168.a3f80f1e.js"><link rel="prefetch" href="/assets/js/169.a6565c29.js"><link rel="prefetch" href="/assets/js/17.d6f2f3b8.js"><link rel="prefetch" href="/assets/js/170.566ddeda.js"><link rel="prefetch" href="/assets/js/171.4fd97bee.js"><link rel="prefetch" href="/assets/js/172.5295c6b9.js"><link rel="prefetch" href="/assets/js/173.ff25fd05.js"><link rel="prefetch" href="/assets/js/174.22ea6a1b.js"><link rel="prefetch" href="/assets/js/175.c6a04367.js"><link rel="prefetch" href="/assets/js/176.d3810924.js"><link rel="prefetch" href="/assets/js/177.facd2802.js"><link rel="prefetch" href="/assets/js/178.f4c6fbe9.js"><link rel="prefetch" href="/assets/js/179.1a3a2c12.js"><link rel="prefetch" href="/assets/js/18.83f6b39a.js"><link rel="prefetch" href="/assets/js/180.b54c8e90.js"><link rel="prefetch" href="/assets/js/181.88495b4c.js"><link rel="prefetch" href="/assets/js/182.8341d23c.js"><link rel="prefetch" href="/assets/js/183.332bcfe2.js"><link rel="prefetch" href="/assets/js/184.d8996a56.js"><link rel="prefetch" href="/assets/js/185.b8584f07.js"><link rel="prefetch" href="/assets/js/186.fa89e23f.js"><link rel="prefetch" href="/assets/js/187.90340eeb.js"><link rel="prefetch" href="/assets/js/188.efe631f8.js"><link rel="prefetch" href="/assets/js/189.72897b1a.js"><link rel="prefetch" href="/assets/js/19.94f071c0.js"><link rel="prefetch" href="/assets/js/190.1ec691ee.js"><link rel="prefetch" href="/assets/js/2.fc5d9892.js"><link rel="prefetch" href="/assets/js/20.13f7bbff.js"><link rel="prefetch" href="/assets/js/21.9ebad2b1.js"><link rel="prefetch" href="/assets/js/22.764b643d.js"><link rel="prefetch" href="/assets/js/23.f533737e.js"><link rel="prefetch" href="/assets/js/24.4eaa533c.js"><link rel="prefetch" href="/assets/js/25.e9877e57.js"><link rel="prefetch" href="/assets/js/26.8905d6a0.js"><link rel="prefetch" href="/assets/js/27.b199a955.js"><link rel="prefetch" href="/assets/js/28.524af0cf.js"><link rel="prefetch" href="/assets/js/29.b772949f.js"><link rel="prefetch" href="/assets/js/3.6b94323a.js"><link rel="prefetch" href="/assets/js/30.f5bcac64.js"><link rel="prefetch" href="/assets/js/31.c55e03aa.js"><link rel="prefetch" href="/assets/js/32.e575600a.js"><link rel="prefetch" href="/assets/js/33.3fff5656.js"><link rel="prefetch" href="/assets/js/34.82598567.js"><link rel="prefetch" href="/assets/js/35.8e1290ba.js"><link rel="prefetch" href="/assets/js/36.6dbb9d8c.js"><link rel="prefetch" href="/assets/js/37.d27fdc88.js"><link rel="prefetch" href="/assets/js/38.f8fc7b20.js"><link rel="prefetch" href="/assets/js/39.bf967d40.js"><link rel="prefetch" href="/assets/js/4.706a5607.js"><link rel="prefetch" href="/assets/js/40.e1864fd6.js"><link rel="prefetch" href="/assets/js/41.61bd3b64.js"><link rel="prefetch" href="/assets/js/42.34d8d61d.js"><link rel="prefetch" href="/assets/js/43.1aee493f.js"><link rel="prefetch" href="/assets/js/44.22177e9b.js"><link rel="prefetch" href="/assets/js/45.5c743d2f.js"><link rel="prefetch" href="/assets/js/46.4feffac9.js"><link rel="prefetch" href="/assets/js/47.367f9103.js"><link rel="prefetch" href="/assets/js/48.0c246d45.js"><link rel="prefetch" href="/assets/js/49.ee354be9.js"><link rel="prefetch" href="/assets/js/5.7d9779f7.js"><link rel="prefetch" href="/assets/js/50.bd81c7f0.js"><link rel="prefetch" href="/assets/js/51.a5f432dd.js"><link rel="prefetch" href="/assets/js/52.537aded4.js"><link rel="prefetch" href="/assets/js/53.84814584.js"><link rel="prefetch" href="/assets/js/54.5f780e97.js"><link rel="prefetch" href="/assets/js/55.9d44c91d.js"><link rel="prefetch" href="/assets/js/56.fc164b43.js"><link rel="prefetch" href="/assets/js/57.959b2d44.js"><link rel="prefetch" href="/assets/js/58.bd5c38e0.js"><link rel="prefetch" href="/assets/js/59.3eaece52.js"><link rel="prefetch" href="/assets/js/6.49a49457.js"><link rel="prefetch" href="/assets/js/60.4b8cb735.js"><link rel="prefetch" href="/assets/js/61.b54b9195.js"><link rel="prefetch" href="/assets/js/62.0116614c.js"><link rel="prefetch" href="/assets/js/63.85b1247d.js"><link rel="prefetch" href="/assets/js/64.8f1be6b5.js"><link rel="prefetch" href="/assets/js/65.6a241647.js"><link rel="prefetch" href="/assets/js/66.08c8bd13.js"><link rel="prefetch" href="/assets/js/67.002e0071.js"><link rel="prefetch" href="/assets/js/68.6c6a7c94.js"><link rel="prefetch" href="/assets/js/69.64d90e5d.js"><link rel="prefetch" href="/assets/js/7.469d3b64.js"><link rel="prefetch" href="/assets/js/70.b2c287c9.js"><link rel="prefetch" href="/assets/js/71.b7ad1135.js"><link rel="prefetch" href="/assets/js/72.7391b5c2.js"><link rel="prefetch" href="/assets/js/73.c5c77d81.js"><link rel="prefetch" href="/assets/js/74.96f80ffd.js"><link rel="prefetch" href="/assets/js/75.2920c7b7.js"><link rel="prefetch" href="/assets/js/76.a90bd3c9.js"><link rel="prefetch" href="/assets/js/77.3a0eeccd.js"><link rel="prefetch" href="/assets/js/78.6791678c.js"><link rel="prefetch" href="/assets/js/79.8b0c78e4.js"><link rel="prefetch" href="/assets/js/8.b36167bf.js"><link rel="prefetch" href="/assets/js/80.2641b89d.js"><link rel="prefetch" href="/assets/js/81.03d1925e.js"><link rel="prefetch" href="/assets/js/82.71061708.js"><link rel="prefetch" href="/assets/js/83.df220a8f.js"><link rel="prefetch" href="/assets/js/84.99fbef8f.js"><link rel="prefetch" href="/assets/js/85.dee8bcae.js"><link rel="prefetch" href="/assets/js/86.5200e4ce.js"><link rel="prefetch" href="/assets/js/87.96df0d82.js"><link rel="prefetch" href="/assets/js/88.e9171a32.js"><link rel="prefetch" href="/assets/js/89.53987bbc.js"><link rel="prefetch" href="/assets/js/9.5bed6ebe.js"><link rel="prefetch" href="/assets/js/90.8da0ff06.js"><link rel="prefetch" href="/assets/js/91.4bcce2cd.js"><link rel="prefetch" href="/assets/js/92.aca18707.js"><link rel="prefetch" href="/assets/js/93.93fdf568.js"><link rel="prefetch" href="/assets/js/94.e893b256.js"><link rel="prefetch" href="/assets/js/95.cb1d62b6.js"><link rel="prefetch" href="/assets/js/96.5529545c.js"><link rel="prefetch" href="/assets/js/97.d01eda68.js"><link rel="prefetch" href="/assets/js/98.384bebd6.js"><link rel="prefetch" href="/assets/js/99.fa349b8f.js"><link rel="prefetch" href="/assets/js/vendors~docsearch.aa72e838.js"> |
| <link rel="stylesheet" href="/assets/css/styles.0ff943f0.css"> |
| </head> |
| <body> |
| <div id="app" data-server-rendered="true"><div class="theme-container vuepress-theme-fast"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div><a href="/zh/" class="home-link router-link-active"><img src="/logo@2x.svg" alt="WEEX" class="logo"></a><span class="version"><a href="/download/download.html" class="version-bg"><img src="/assets/img/version-bg.ab65ded9.svg" alt="v0.28"><span class="version-no">v0.28</span></a></span><div class="links"><form id="search-form" class="algolia-search-wrapper search-box"><input id="algolia-search-input" class="search-query"></form><nav class="nav-links can-hide"><div class="nav-item"><a href="/zh/guide/" class="nav-link">指南</a></div><div class="nav-item"><a href="/zh/docs/" class="nav-link">文档</a></div><div class="nav-item"><a href="/zh/tools/" class="nav-link">第三方工具</a></div><div class="nav-item"><a href="/zh/download/" class="nav-link">下载</a></div><div class="nav-item"><a href="/zh/community/" class="nav-link">社区</a></div><div class="nav-item"><a href="/zh/blog/" class="nav-link router-link-active">博客</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title">选择语言</span><span class="arrow right"></span></a><ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----><a href="/" class="nav-link">English</a></li><li class="dropdown-item"><!----><a href="/zh/blog/weex-auto-test-locating.html" class="nav-link router-link-exact-active router-link-active">简体中文</a></li></ul></div></div></nav><a href="https://github.com/apache/incubator-weex" target="_blank" rel="noopener noreferrer" class="repo-link"><img src="/assets/img/github.7cb484a9.svg" alt="github" class="github-icon"></a></div></header><div class="sidebar-mask"></div><div class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/zh/guide/" class="nav-link">指南</a></div><div class="nav-item"><a href="/zh/docs/" class="nav-link">文档</a></div><div class="nav-item"><a href="/zh/tools/" class="nav-link">第三方工具</a></div><div class="nav-item"><a href="/zh/download/" class="nav-link">下载</a></div><div class="nav-item"><a href="/zh/community/" class="nav-link">社区</a></div><div class="nav-item"><a href="/zh/blog/" class="nav-link router-link-active">博客</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title">选择语言</span><span class="arrow right"></span></a><ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----><a href="/" class="nav-link">English</a></li><li class="dropdown-item"><!----><a href="/zh/blog/weex-auto-test-locating.html" class="nav-link router-link-exact-active router-link-active">简体中文</a></li></ul></div></div></nav><ul class="sidebar-links"><li><a href="/zh/blog/write-a-blog.html" class="sidebar-link">写一篇博客</a></li><li><a href="/zh/blog/weex-auto-test-locating.html" class="active sidebar-link">Weex自动化测试元素定位方案</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/zh/blog/weex-auto-test-locating.html#背景介绍" class="sidebar-link">背景介绍</a></li><li class="sidebar-sub-header"><a href="/zh/blog/weex-auto-test-locating.html#解决方案" class="sidebar-link">解决方案</a></li></ul></li><li><a href="/zh/blog/ios-weexcore.html" class="sidebar-link">iOS WeexSDK 接入 WeexCore</a></li><li><a href="/zh/blog/weexcore-multiprocess-evolution.html" class="sidebar-link">WeexCore 多进程多线程架构演进</a></li><li><a href="/zh/blog/interaction-optimization.html" class="sidebar-link">可交互时间的探索和首屏时间的改进之路</a></li><li><a href="/zh/blog/what_is_the_most_important_assest_to_an_open_souce_project.html" class="sidebar-link">什么是一个开源项目最重要的资产?</a></li><li><a href="/zh/blog/weex-third-party-extensions.html" class="sidebar-link">Weex 三方插件</a></li></ul></div><div class="page"><div class="content"><h2 id="背景介绍"><a href="#背景介绍" class="header-anchor">#</a> 背景介绍</h2><p>在WeexSDK的日常测试和自动化沉淀过程中,作为SDK的测试同学会遇到一下的问题:</p><ol><li>手淘Weex业务众多,并且业务逻辑复杂,人工回归成本高、效率低,并且在手工全覆盖的情况下,因为对业务逻辑的不了解,仍然会有“漏网”的bug。</li><li>对于Weex底层组件的自动化测试中,元素定位是一个比较麻烦的问题。由于Weex特殊的渲染逻辑,不会为每个元素分配一个唯一的id进行区分,在自动化脚本编写过程中,定位元素的成本和稳定性受到考验。</li><li>现有的测试框架,都无法彻底磨平Android、iOS两大平台的差异。作为一个跨平台的开发框架,配套的自动化测试,也能够从底层磨平两个平台的差异,做到一套代码,两端执行。</li></ol><p>针对上述问题,我们在手淘中落地了一个方便快捷的元素定位方案,能够让Weex业务测试同学在前端配合下为指定元素分配一个唯一的ID,方便定位。</p><h2 id="解决方案"><a href="#解决方案" class="header-anchor">#</a> 解决方案</h2><p>在手淘上的元素定位问题,我们参考了@歪木在weex playground中的一个实现方案,为Weex页面提供了一个新的test-id属性,可以方便UI自动化脚本唯一性的快速定位指定元素。具体方案和使用方法如下:</p><h3 id="android"><a href="#android" class="header-anchor">#</a> Android</h3><hr><p><strong>实现原理</strong></p><p><img src="/blog/weex-auto-test-locating/1531211875576-24be9339-cb25-41fc-8b4c-26d74aaf1ddd.png" alt="image.png"></p><p>如上图所示,我们首先在weex页面的根部,创建了一个container节点,用于存储当前页面所有添加test-id属性的节点其test-id值与实际节点ID的映射关系;其次,在页面的layout发生变化时,触发收集新的携带test-id节点的信息,并为其分配一个新的ID;最后,我们将该节点的test-id属性和对应的ID写入到container节点中,方便自动化脚本查询指定节点时,能够获取其真实ID的信息;</p><p><strong>主要代码</strong></p><ul><li>由于Android不支持动态的创建节点ID,只能动态的将view与ID绑定,因此我们预先注册了5000个备用的ID,来作为与weex页面view绑定而使用的ID。</li><li>客户端中通过监听页面Layout变化,触发遍历查找testId属性的逻辑。</li></ul><div class="language-java extra-class"><pre class="language-java"><code><span class="token comment">//Weex页面onCreateView回调时触发,监听当前页面Layout变化,触发执行collectIdTask()</span> |
| <span class="token annotation punctuation">@Override</span> |
| <span class="token keyword">public</span> <span class="token class-name">View</span> <span class="token function">onCreateView</span><span class="token punctuation">(</span><span class="token class-name">WXSDKInstance</span> instance<span class="token punctuation">,</span> <span class="token class-name">View</span> view<span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token class-name">View</span> wrappedView <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> |
| observer <span class="token operator">=</span> view<span class="token punctuation">.</span><span class="token function">getViewTreeObserver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
| mInstance <span class="token operator">=</span> instance<span class="token punctuation">;</span> |
| <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token class-name">WXEnvironment</span><span class="token punctuation">.</span><span class="token function">isApkDebugable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token keyword">if</span> <span class="token punctuation">(</span>instance<span class="token punctuation">.</span><span class="token function">getContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">instanceof</span> <span class="token class-name">WXActivity</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| listener <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ViewTreeObserver</span><span class="token punctuation">.</span><span class="token class-name">OnGlobalLayoutListener</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token annotation punctuation">@Override</span> |
| <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onGlobalLayout</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token keyword">if</span><span class="token punctuation">(</span>layoutChangeSignal <span class="token operator">==</span> <span class="token boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token keyword">return</span><span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| <span class="token keyword">else</span> <span class="token punctuation">{</span> |
| layoutChangeSignal <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> |
| <span class="token keyword">new</span> <span class="token function">collectIdTask</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| <span class="token punctuation">}</span> |
| <span class="token punctuation">}</span><span class="token punctuation">;</span> |
| observer<span class="token punctuation">.</span><span class="token function">addOnGlobalLayoutListener</span><span class="token punctuation">(</span>listener<span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| <span class="token punctuation">}</span> |
| <span class="token keyword">if</span> <span class="token punctuation">(</span>mAnalyzerDelegate <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| wrappedView <span class="token operator">=</span> mAnalyzerDelegate<span class="token punctuation">.</span><span class="token function">onWeexViewCreated</span><span class="token punctuation">(</span>instance<span class="token punctuation">,</span> view<span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| <span class="token keyword">if</span> <span class="token punctuation">(</span>wrappedView <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token keyword">return</span> view<span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| <span class="token keyword">return</span> wrappedView<span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| </code></pre></div><ul><li>对于一个携带testId属性的节点,为其分配一个nativeId。将testId属性值与nativeId的映射关系写到页面的根节点处。将nativeId动态的赋值到当前节点中。</li></ul><div class="language-java extra-class"><pre class="language-java"><code><span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">collectId</span><span class="token punctuation">(</span><span class="token class-name">WXComponent</span> comp<span class="token punctuation">,</span> <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">String</span><span class="token punctuation">></span></span> map<span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token keyword">if</span> <span class="token punctuation">(</span>comp <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token keyword">return</span><span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| <span class="token class-name">String</span> id<span class="token punctuation">;</span> |
| <span class="token class-name">View</span> view <span class="token punctuation">;</span> |
| <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>id <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">)</span> comp<span class="token punctuation">.</span><span class="token function">getAttrs</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"testId"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> <span class="token punctuation">(</span>view <span class="token operator">=</span> comp<span class="token punctuation">.</span><span class="token function">getHostView</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token keyword">null</span> |
| <span class="token operator">&&</span> <span class="token operator">!</span>map<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token class-name">Pair</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">Integer</span><span class="token punctuation">></span></span> pair <span class="token operator">=</span> <span class="token class-name">Utility</span><span class="token punctuation">.</span><span class="token function">nextID</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
| view<span class="token punctuation">.</span><span class="token function">setId</span><span class="token punctuation">(</span>pair<span class="token punctuation">.</span>second<span class="token punctuation">)</span><span class="token punctuation">;</span> |
| map<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span> pair<span class="token punctuation">.</span>first<span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| <span class="token keyword">if</span> <span class="token punctuation">(</span>comp <span class="token keyword">instanceof</span> <span class="token class-name">WXVContainer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token class-name">WXVContainer</span> container <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">WXVContainer</span><span class="token punctuation">)</span> comp<span class="token punctuation">;</span> |
| <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> container<span class="token punctuation">.</span><span class="token function">getChildCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">;</span> i<span class="token operator">--</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token function">collectId</span><span class="token punctuation">(</span>container<span class="token punctuation">.</span><span class="token function">getChild</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">,</span> map<span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| <span class="token punctuation">}</span> |
| <span class="token punctuation">}</span> |
| |
| <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">collectIDMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token keyword">if</span> <span class="token punctuation">(</span>mInstance<span class="token punctuation">.</span><span class="token function">getContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">instanceof</span> <span class="token class-name">WXActivity</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token class-name">WXActivity</span> appCompatActivity <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">WXActivity</span><span class="token punctuation">)</span> mInstance<span class="token punctuation">.</span><span class="token function">getContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token class-name">View</span> container <span class="token operator">=</span> appCompatActivity<span class="token punctuation">.</span><span class="token function">findViewById</span><span class="token punctuation">(</span><span class="token class-name">R</span><span class="token punctuation">.</span>id<span class="token punctuation">.</span>container_test_id<span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token function">collectId</span><span class="token punctuation">(</span>mInstance<span class="token punctuation">.</span><span class="token function">getRootComponent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> mIDMap<span class="token punctuation">)</span><span class="token punctuation">;</span> |
| container<span class="token punctuation">.</span><span class="token function">setContentDescription</span><span class="token punctuation">(</span>JSON<span class="token punctuation">.</span><span class="token function">toJSONString</span><span class="token punctuation">(</span>mIDMap<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| <span class="token punctuation">}</span> |
| </code></pre></div><h3 id="ios"><a href="#ios" class="header-anchor">#</a> iOS</h3><p>相对于android,weex实现iOS的ById方式就简单多了。做过iOS自动化的同学应该听说过 iOS Accessibility,苹果为障碍人群提供的友好交互功能;iOS UI自动化正好可以与accessibility结合起来进行元素定位,参见:<a href="https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/09-ui_testing.html" target="_blank" rel="noopener noreferrer">User Interface Testing<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a><a href="https://developer.apple.com/documentation/uikit/uiaccessibilityidentification" target="_blank" rel="noopener noreferrer">accessibilityidentifer<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a> 是Accessibility的一种方式,用来唯一地标识一个元素使用UI自动化脚本编写的接口。</p><div class="language- extra-class"><pre class="language-text"><code>An identifier can be used to uniquely identify an element in the scripts you write using the UI Automation interfaces. |
| Using an identifier allows you to avoid inappropriately setting or accessing an element’s accessibility label. |
| </code></pre></div><p>所以我们只需要在创建view的同时,将test-id作为identifier设置给对应的节点,UI自动化测试时就可以通过ById的方式获得对应的节点了。</p><p>此处代码已经在weexsdk中实现,不需要业务方做任何额外实现。 |
| 代码如下:</p><div class="language- extra-class"><pre class="language-text"><code>// 前端的test-id传输到native端会默认转化成testId |
| if (attributes[@"testId"]) { |
| [self.view setAccessibilityIdentifier:[WXConvert NSString:attributes[@"testId"]]]; |
| } |
| </code></pre></div><hr><h3 id="前端实践案例"><a href="#前端实践案例" class="header-anchor">#</a> 前端实践案例</h3><p>下面给出Weex最常见的Vue页面中的使用demo</p><p>对于Vue来说,由于在其内部,会将“XXX-XXX”格式的属性转为驼峰形式命名方式。因此,为了遵从http的命名方式,我们建议在节点属性命名时,使用“test-id”作为元素定位的属性。</p><div class="language- extra-class"><pre class="language-text"><code><template> |
| <div style="align-items:center"> |
| <text test-id='text' class="title">Weex 官网</text> |
| <div class="wrapper"> |
| <web class="webview" src="http://weex-project.io/"></web> |
| </div> |
| </div> |
| </template> |
| |
| <style scoped> |
| .title { |
| font-size: 60px; |
| margin-top: 40px; |
| margin-bottom: 20px; |
| color: #1B90F7; |
| } |
| .wrapper { |
| width: 704px; |
| height: 884px; |
| border-width: 2px; |
| border-style: solid; |
| border-color: #1B90F7; |
| } |
| .webview { |
| width: 700px; |
| height: 880px; |
| } |
| </style> |
| </code></pre></div><h3 id="测试脚本"><a href="#测试脚本" class="header-anchor">#</a> 测试脚本</h3><p>由于iOS下,只需要获取“testID”属性即可,此处我们不在赘述在iOS下定位带元素的方法。下面,我们将给出Android下查找test-id的测试代码。而业务方在实际使用过程当中,可以在自己的自动化框架中,封装统一的FindByTestID方法,来达到一套代码双端执行的效果。后续,我们也会对外推出一个基于Appium的测试框架,提供一个功能较为丰富的weex自动化执行框架给外部开发者。</p><p><strong>原理示意图</strong><img src="/blog/weex-auto-test-locating/1531211945217-fe2a8501-dfd8-4372-a3e5-b5007e5bced2.png" alt="image.png"></p><p>针对本方案,我们基于Appium测试框架,实现了对应查找元素的方法。代码逻辑如下:</p><div class="language-java extra-class"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">WebElement</span> <span class="token function">waitForElementByTestID</span><span class="token punctuation">(</span><span class="token class-name">String</span> testId<span class="token punctuation">,</span> <span class="token keyword">int</span> waitTime<span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token keyword">long</span> startTime <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
| |
| <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> startTime<span class="token punctuation">)</span> <span class="token operator"><</span> waitTime<span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token class-name">WebElement</span> container <span class="token operator">=</span> <span class="token function">waitForVisible</span><span class="token punctuation">(</span><span class="token class-name">By</span><span class="token punctuation">.</span><span class="token function">id</span><span class="token punctuation">(</span><span class="token string">"com.taobao.taobao:id/container_test_id"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">5000</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token keyword">if</span> <span class="token punctuation">(</span>container <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token class-name">JSONObject</span> idMap <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">JSONObject</span><span class="token punctuation">)</span> JSON<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>container<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token keyword">if</span><span class="token punctuation">(</span>idMap<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>testId<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token keyword">continue</span><span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| <span class="token class-name">WebElement</span> element <span class="token operator">=</span> <span class="token function">waitForVisible</span><span class="token punctuation">(</span><span class="token class-name">By</span><span class="token punctuation">.</span><span class="token function">id</span><span class="token punctuation">(</span><span class="token string">"com.taobao.taobao:id/"</span> <span class="token operator">+</span> idMap<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>testId<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token keyword">if</span><span class="token punctuation">(</span>element <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| logger<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"native id found!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token keyword">return</span> element<span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| <span class="token punctuation">}</span> |
| <span class="token punctuation">}</span> |
| <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| |
| <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">WebElement</span> <span class="token function">waitForVisible</span><span class="token punctuation">(</span><span class="token class-name">By</span> by<span class="token punctuation">,</span> <span class="token keyword">int</span> waitTime<span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token class-name">WebElement</span> el <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> |
| <span class="token class-name">WebDriverWait</span> wait <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">WebDriverWait</span><span class="token punctuation">(</span>driver<span class="token punctuation">,</span> waitTime<span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> attempt <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> attempt <span class="token operator"><</span> waitTime<span class="token punctuation">;</span> attempt<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token keyword">try</span> <span class="token punctuation">{</span> |
| el <span class="token operator">=</span> <span class="token function">getDriver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">findElement</span><span class="token punctuation">(</span>by<span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token keyword">break</span><span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> |
| <span class="token function">getDriver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">manage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">timeouts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">implicitlyWait</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token class-name">TimeUnit</span><span class="token punctuation">.</span>SECONDS<span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| <span class="token punctuation">}</span> |
| wait<span class="token punctuation">.</span><span class="token function">until</span><span class="token punctuation">(</span><span class="token class-name">ExpectedConditions</span><span class="token punctuation">.</span><span class="token function">visibilityOfElementLocated</span><span class="token punctuation">(</span>by<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
| <span class="token keyword">return</span> el<span class="token punctuation">;</span> |
| <span class="token punctuation">}</span> |
| </code></pre></div></div><div class="page-nav"><p class="inner"><span class="prev"> |
| ← |
| <a href="/zh/blog/write-a-blog.html" class="prev"> |
| 写一篇博客 |
| </a></span><span class="next"><a href="/zh/blog/ios-weexcore.html"> |
| iOS WeexSDK 接入 WeexCore |
| </a> |
| → |
| </span></p></div><div class="page-edit"><div class="last-updated"><span class="prefix">上次更新: </span><span class="time">2020/12/10</span></div><div class="edit-link"><a href="https://github.com/apache/incubator-weex-site/edit/master/docs/zh/blog/weex-auto-test-locating.md" target="_blank" rel="noopener noreferrer">编辑此页</a><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><a href="https://github.com/apache/incubator-weex-site/issues/new?body=https%3A%2F%2Fweex.io%2F" target="_blank" rel="noopener noreferrer" class="issueText">提交建议</a><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></div><div class="score"><span class="choice"><img src="https://img.alicdn.com/tfs/TB1h7TSqpYqK1RjSZLeXXbXppXa-20-18.svg" class="score-icon">文档写得很棒</span><span class="choice"><img src="https://img.alicdn.com/tfs/TB1h7TSqpYqK1RjSZLeXXbXppXa-20-18.svg" class="score-icon bad">文档写得很差</span></div></div><div class="license-wrap" style="display:none;"><footer class="footer-container" data-v-0ad13d84><div class="footer-body" data-v-0ad13d84><img src="/logo@2x.svg" class="logo" data-v-0ad13d84><img src="//img.alicdn.com/tfs/TB11BRiIW6qK1RjSZFmXXX0PFXa-365-365.png" class="apache" data-v-0ad13d84><div class="cols-container" data-v-0ad13d84><div class="col col-12" data-v-0ad13d84><h3 data-v-0ad13d84>Disclaimer</h3><p data-v-0ad13d84>Apache Weex is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the |
| Incubator. Incubation is required of all newly accepted projects until a further review indicates that the |
| infrastructure, communications, and decision making process have stabilized in a manner consistent with |
| other successful ASF projects. While incubation status is not necessarily a reflection of the completeness |
| or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF.</p></div><div class="col col-4" data-v-0ad13d84><dl data-v-0ad13d84><dt data-v-0ad13d84>ASF</dt><dd data-v-0ad13d84><a href="http://www.apache.org" target="_self" data-v-0ad13d84>Foundation</a></dd><dd data-v-0ad13d84><a href="http://www.apache.org/licenses/" target="_self" data-v-0ad13d84>License</a></dd><dd data-v-0ad13d84><a href="http://www.apache.org/events/current-event" target="_self" data-v-0ad13d84>Events</a></dd><dd data-v-0ad13d84><a href="http://www.apache.org/foundation/sponsorship.html" target="_self" data-v-0ad13d84>Sponsorship</a></dd><dd data-v-0ad13d84><a href="http://www.apache.org/foundation/thanks.html" target="_self" data-v-0ad13d84>Thanks</a></dd></dl></div><div class="col col-4" data-v-0ad13d84><dl data-v-0ad13d84><dt data-v-0ad13d84>Documentation</dt><dd data-v-0ad13d84><a href="/guide/develop/create-a-new-app.html" target="_self" data-v-0ad13d84>Quick start</a></dd><dd data-v-0ad13d84><a href="/guide/develop/setup-develop-environment.html" target="_self" data-v-0ad13d84>Developer guide</a></dd></dl></div><div class="col col-4" data-v-0ad13d84><dl data-v-0ad13d84><dt data-v-0ad13d84>Resources</dt><dd data-v-0ad13d84><a href="/blog/write-a-blog.html" target="_self" data-v-0ad13d84>Blog</a></dd><dd data-v-0ad13d84><a href="/community/" target="_self" data-v-0ad13d84>Community</a></dd><dd data-v-0ad13d84><a href="/guide/contribute/thanks.html" target="_self" data-v-0ad13d84>Thanks by Weex</a></dd><dd data-v-0ad13d84><a href="https://www.apache.org/security/" target="_self" data-v-0ad13d84>Security</a></dd></dl></div></div><div class="copyright" data-v-0ad13d84><span data-v-0ad13d84>Copyright © 2018-2019 The Apache Software Foundation. Apache and the Apache feather |
| logo are trademarks of The Apache Software Foundation.</span></div></div></footer></div></div><!----></div></div> |
| <script src="/assets/js/app.0ff943f0.js" defer></script><script src="/assets/js/115.d7e38c3b.js" defer></script> |
| </body> |
| </html> |