You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by ha...@apache.org on 2018/12/21 02:38:50 UTC

[incubator-weex-site] branch draft updated: add blog zh content (#261)

This is an automated email from the ASF dual-hosted git repository.

hanks pushed a commit to branch draft
in repository https://gitbox.apache.org/repos/asf/incubator-weex-site.git


The following commit(s) were added to refs/heads/draft by this push:
     new fea6061  add blog zh content (#261)
fea6061 is described below

commit fea606166d780dc8079486afe400cb6e6d9a3ce8
Author: jasonintju <ja...@gmail.com>
AuthorDate: Fri Dec 21 10:38:47 2018 +0800

    add blog zh content (#261)
    
    * add zh blog content
    
    * optimize styles
---
 docs/.vuepress/components/IPhoneImg.vue |  32 ------
 docs/.vuepress/override.styl            |   3 +-
 docs/zh/blog/weex-auto-test-locating.md | 183 +++++++++++++++++++++++++++++++-
 3 files changed, 184 insertions(+), 34 deletions(-)

diff --git a/docs/.vuepress/components/IPhoneImg.vue b/docs/.vuepress/components/IPhoneImg.vue
deleted file mode 100644
index c815f29..0000000
--- a/docs/.vuepress/components/IPhoneImg.vue
+++ /dev/null
@@ -1,32 +0,0 @@
-<template>
-  <div class="wrapper">
-    <div class="iPhone-img">
-      <img src="https://img.alicdn.com/tfs/TB1dZ.4nY2pK1RjSZFsXXaNlXXa-435-895.svg" width="323">
-      <img class="img" :src="$withBase(imgSrc)" width="284" height="505">
-    </div>
-  </div>
-</template>
-
-<script>
-export default {
-  props: ['imgSrc']
-}
-</script>
-
-<style>
-.wrapper {
-  position: relative;
-  height: 644px;
-}
-.iPhone-img {
-  position: absolute;
-  left: 50%;
-  top: 0;
-  transform: translateX(-50%);
-}
-.img {
-  position: absolute;
-  left: 17px;
-  top: 81px;
-}
-</style>
\ No newline at end of file
diff --git a/docs/.vuepress/override.styl b/docs/.vuepress/override.styl
index 5b1cb96..3fd8746 100644
--- a/docs/.vuepress/override.styl
+++ b/docs/.vuepress/override.styl
@@ -1 +1,2 @@
-#app .sidebar { width: 15rem; }
\ No newline at end of file
+#app .sidebar { width: 15rem; }
+#app .page { padding-bottom: 0; }
\ No newline at end of file
diff --git a/docs/zh/blog/weex-auto-test-locating.md b/docs/zh/blog/weex-auto-test-locating.md
index 895429a..625eed5 100644
--- a/docs/zh/blog/weex-auto-test-locating.md
+++ b/docs/zh/blog/weex-auto-test-locating.md
@@ -1 +1,182 @@
-# 博客
\ No newline at end of file
+## 背景介绍
+在WeexSDK的日常测试和自动化沉淀过程中,作为SDK的测试同学会遇到一下的问题:
+1. 手淘Weex业务众多,并且业务逻辑复杂,人工回归成本高、效率低,并且在手工全覆盖的情况下,因为对业务逻辑的不了解,仍然会有“漏网”的bug。
+2. 对于Weex底层组件的自动化测试中,元素定位是一个比较麻烦的问题。由于Weex特殊的渲染逻辑,不会为每个元素分配一个唯一的id进行区分,在自动化脚本编写过程中,定位元素的成本和稳定性受到考验。
+3. 现有的测试框架,都无法彻底磨平Android、iOS两大平台的差异。作为一个跨平台的开发框架,配套的自动化测试,也能够从底层磨平两个平台的差异,做到一套代码,两端执行。
+
+针对上述问题,我们在手淘中落地了一个方便快捷的元素定位方案,能够让Weex业务测试同学在前端配合下为指定元素分配一个唯一的ID,方便定位。
+## 解决方案
+在手淘上的元素定位问题,我们参考了@歪木在weex playground中的一个实现方案,为Weex页面提供了一个新的test-id属性,可以方便UI自动化脚本唯一性的快速定位指定元素。具体方案和使用方法如下:
+### Android
+---
+**实现原理**
+
+![image.png](https://cdn.yuque.com/lark/0/2018/png/34622/1531211875576-24be9339-cb25-41fc-8b4c-26d74aaf1ddd.png)
+
+如上图所示,我们首先在weex页面的根部,创建了一个container节点,用于存储当前页面所有添加test-id属性的节点其test-id值与实际节点ID的映射关系;其次,在页面的layout发生变化时,触发收集新的携带test-id节点的信息,并为其分配一个新的ID;最后,我们将该节点的test-id属性和对应的ID写入到container节点中,方便自动化脚本查询指定节点时,能够获取其真实ID的信息;
+
+**主要代码**
+- 由于Android不支持动态的创建节点ID,只能动态的将view与ID绑定,因此我们预先注册了5000个备用的ID,来作为与weex页面view绑定而使用的ID。
+- 客户端中通过监听页面Layout变化,触发遍历查找testId属性的逻辑。
+```java
+//Weex页面onCreateView回调时触发,监听当前页面Layout变化,触发执行collectIdTask()
+@Override
+public View onCreateView(WXSDKInstance instance, View view) {
+    View wrappedView = null;
+    observer = view.getViewTreeObserver();
+    mInstance = instance;
+    if (WXEnvironment.isApkDebugable()) {
+        if (instance.getContext() instanceof WXActivity) {
+            listener = new ViewTreeObserver.OnGlobalLayoutListener() {
+                @Override
+                public void onGlobalLayout() {
+                    if(layoutChangeSignal == false) {
+                        return;
+                    }
+                    else {
+                        layoutChangeSignal = false;
+                        new collectIdTask().execute();
+                    }
+                }
+            };
+            observer.addOnGlobalLayoutListener(listener);
+        }
+    }
+    if (mAnalyzerDelegate != null) {
+        wrappedView = mAnalyzerDelegate.onWeexViewCreated(instance, view);
+    }
+    if (wrappedView == null) {
+        return view;
+    }
+    return wrappedView;
+}
+ ```    
+ - 对于一个携带testId属性的节点,为其分配一个nativeId。将testId属性值与nativeId的映射关系写到页面的根节点处。将nativeId动态的赋值到当前节点中。
+ ```java
+ private static void collectId(WXComponent comp, Map<String, String> map) {
+    if (comp == null) {
+        return;
+    }
+    String id;
+    View view ;
+    if ((id = (String) comp.getAttrs().get("testId")) != null && (view = comp.getHostView()) != null
+                    && !map.containsKey(id)) {
+        Pair<String, Integer> pair = Utility.nextID();
+        view.setId(pair.second);
+        map.put(id, pair.first);
+    }
+            if (comp instanceof WXVContainer) {
+                WXVContainer container = (WXVContainer) comp;
+                for (int i = container.getChildCount() - 1; i >= 0; i--) {
+                    collectId(container.getChild(i), map);
+                }
+            }
+        }
+
+private void collectIDMap() {
+    if (mInstance.getContext() instanceof WXActivity) {
+         WXActivity appCompatActivity = (WXActivity) mInstance.getContext();
+         View container = appCompatActivity.findViewById(R.id.container_test_id);
+	     collectId(mInstance.getRootComponent(), mIDMap);
+         container.setContentDescription(JSON.toJSONString(mIDMap));
+     }
+ }
+ ```
+
+### iOS
+相对于android,weex实现iOS的ById方式就简单多了。做过iOS自动化的同学应该听说过 iOS Accessibility,苹果为障碍人群提供的友好交互功能;iOS UI自动化正好可以与accessibility结合起来进行元素定位,参见:[User Interface Testing](https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/09-ui_testing.html)
+[accessibilityidentifer](https://developer.apple.com/documentation/uikit/uiaccessibilityidentification) 是Accessibility的一种方式,用来唯一地标识一个元素使用UI自动化脚本编写的接口。
+```
+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.
+```
+所以我们只需要在创建view的同时,将test-id作为identifier设置给对应的节点,UI自动化测试时就可以通过ById的方式获得对应的节点了。
+
+此处代码已经在weexsdk中实现,不需要业务方做任何额外实现。
+代码如下:
+```
+// 前端的test-id传输到native端会默认转化成testId
+if (attributes[@"testId"]) {
+    [self.view setAccessibilityIdentifier:[WXConvert NSString:attributes[@"testId"]]];
+}
+```
+ 
+ ---
+### 前端实践案例
+下面给出Weex最常见的Vue页面中的使用demo
+
+对于Vue来说,由于在其内部,会将“XXX-XXX”格式的属性转为驼峰形式命名方式。因此,为了遵从http的命名方式,我们建议在节点属性命名时,使用“test-id”作为元素定位的属性。
+```
+<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>
+```
+
+### 测试脚本
+由于iOS下,只需要获取“testID”属性即可,此处我们不在赘述在iOS下定位带元素的方法。下面,我们将给出Android下查找test-id的测试代码。而业务方在实际使用过程当中,可以在自己的自动化框架中,封装统一的FindByTestID方法,来达到一套代码双端执行的效果。后续,我们也会对外推出一个基于Appium的测试框架,提供一个功能较为丰富的weex自动化执行框架给外部开发者。
+
+**原理示意图**
+![image.png](https://cdn.yuque.com/lark/0/2018/png/34622/1531211945217-fe2a8501-dfd8-4372-a3e5-b5007e5bced2.png)
+
+针对本方案,我们基于Appium测试框架,实现了对应查找元素的方法。代码逻辑如下:
+```java
+public static WebElement waitForElementByTestID(String testId, int waitTime) {
+        long startTime = System.currentTimeMillis();
+
+        while((System.currentTimeMillis() - startTime) < waitTime) {
+            WebElement container = waitForVisible(By.id("com.taobao.taobao:id/container_test_id"), 5000);
+            if (container != null) {
+                JSONObject idMap = (JSONObject) JSON.parse(container.getAttribute("name"));
+                if(idMap.get(testId) == null) {
+                    continue;
+                }
+                WebElement element = waitForVisible(By.id("com.taobao.taobao:id/" + idMap.get(testId)), 3000);
+                if(element != null) {
+                    logger.info("native id found!");
+                    return element;
+                }
+            }
+        }
+        return null;
+    }
+    
+public static WebElement waitForVisible(By by, int waitTime) {
+    WebElement el = null;
+    WebDriverWait wait = new WebDriverWait(driver, waitTime);
+    for (int attempt = 0; attempt < waitTime; attempt++) {
+        try {
+            el = getDriver().findElement(by);
+            break;
+        } catch (Exception e) {
+            getDriver().manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
+        }
+    }
+    wait.until(ExpectedConditions.visibilityOfElementLocated(by));
+    return el;
+}
+```