基于Virtual DOM与Diff DOM的测试代码天生

只管是在岁终,而且也还没把书翻译完,也还没写完书的第一稿。然则,我照样以为这是一个异常不错的话题——测试代码天生。

当我们在写一些UI测试的时刻,我们总须要到浏览器去看一下一些DOM的变化。比方,我们点击了某个下拉菜单,会有别的一个联动的下拉菜单发生了变化。而假如这个事宜更庞杂的时刻,偶然我们能够就很难观察出来他们之间的变化。

Virtual DOM

只管这里的例子是以Jasmine作为例子,然则我想关于React也会有一样的要领。

一个Jasmine jQuery测试

以下是一个简朴的Jamine jQuery的测试示例:

  describe("toHaveCss", function (){
    beforeEach(function (){
      setFixtures(sandbox())
    })

    it("should pass if the element has matching css", function (){
      $("#sandbox").css("display", "none")
      $("#sandbox").css("margin-left", "10px")
      expect($("#sandbox")).toHaveCss({display: "none", "margin-left": "10px"})
    })
});

在beforeEach的时刻,我们设定了牢固的DOM进去,依据用户的行动做一些响应的操纵。接着依据这个DOM中的元素变化 ,来作一些断言。

那末,纵然我们已经有一个牢固的DOM,想要监听这个DOM的变化就是一件轻易的事。在我们断言之前,我们就会有一个新的DOM。我们只须要Diff一下这两个DOM的变化,就能够天生这部份测试代码。

virtual-dom与HyperScript

在寻找中发现了virtual-dom这个库,一个能够支撑建立元素、diff盘算以及patch操纵的库,而且它效力彷佛还不错。

virtual-dom能够说由下面几部份构成的:

  1. createElement,用于建立virtual Node。

  2. diff,望文生义,diff算法。

  3. h,用于建立假造树的DSL——HyperScript。HyperScript是一个JavaScript的HyperText。

  4. patch,用于patch修正的内容。

举例来说,我们有下面一个天生Virtual DOM的函数:

function render(count)  {
    return h('div', {
        style: {
            textAlign: 'center',
            lineHeight: (100 + count) + 'px',
            border: '1px solid red',
            width: (100 + count) + 'px',
            height: (100 + count) + 'px'
        }
    }, [String(count)]);
}

render函数用于天生一个Virtual Node。在这里,我们能够将我们的变量传进去,如1。就会天生以下图所示的节点:

{
    "children": [
        {
            "text": "1"
        }
    ],
    "count": 1,
    "descendantHooks": false,
    "hasThunks": false,
    "hasWidgets": false,
    "namespace": null,
    "properties": {
        "style": {
            "border": "1px solid red",
            "height": "101px",
            "lineHeight": "101px",
            "textAlign": "center",
            "width": "101px"
        }
    },
    "tagName": "DIV"
}

个中包括中相对应的属性等等。而我们只需挪用createElement就能够建立出这个DOM。

假如我们修正了这个节点的一些元素,或许我们render了一个count=2的值时,我们就能够diff两个DOM。如:

virtualDom.diff(render(2), render(1))

依据两个值的变化就会天生以下的一个对象:

{
    "0": {
        "patch": {
            "style": {
                "height": "101px",
                "lineHeight": "101px",
                "width": "101px"
            }
        },
        "type": 4,
        "vNode": {
            ...
        }
    },
    "1": {
        "patch": {
            "text": "1"
        },
        "type": 1,
        "vNode": {
            "text": "2"
        }
    },
    ...
}

第一个对象,即0中包括了一些属性的变化。而第二个则是文本的变化——从2变成了1。我们所要做的测试天生就是标记这些变化,并纪录之。

标记DOM变化

因为virtual-dom依赖于假造节点vNode,我们须要将fixtures转换为hyperscript。这里我们就须要一个名为html2hyperscript的插件,来剖析html。接着,我们就能够diff转换完后的DOM:

var leftNode = "", rightNode = "";
var fixtures = '<div id="example"><h1 class="hello">Hello World</h1></div>';
var change = '<div id="example"><h1 class="hello">Hello World</h1><h2>fs</h2></div>';
parser(fixtures, function (err, hscript) {
  leftNode = eval(hscript);
});

parser(change, function (err, hscript) {
  rightNode = eval(hscript);
});

var patches = diff(leftNode, rightNode);

接着,我们须要挪用patch函数来做一些响应的转变。

luffa.patch(virtualDom.create(leftNode), patches)

而且,我们能够尝试在patch阶段做一些处置惩罚——输出修正:

function printChange(originRootNodeHTML, applyNode) {
  var patchType;

  for (var patchIndex = 0; patchIndex < applyNode.newNodes.length; patchIndex++) {
    patchType = applyNode.newNodes[patchIndex].method;
    switch (patchType) {
      case 'insert':
        printInsert(applyNode);
        break;
      case 'node':
        printNode(applyNode, originRootNodeHTML, patchIndex);
        break;
      case 'remove':
        printRemove(applyNode, originRootNodeHTML, patchIndex);
        break;
      case 'string':
        printString(applyNode, originRootNodeHTML, patchIndex);
        break;
      case 'prop':
        printProp(applyNode, originRootNodeHTML, patchIndex);
        break;
      default:
        printDefault(applyNode, originRootNodeHTML, patchIndex);
    }
  }
}

依据差别的范例,作一些对应的输出处置惩罚,如pringNode:

function printNode(applyNode, originRootNodeHTML, patchIndex) {
  var originNode = $(applyNode.newNodes[patchIndex].vNode).prop('outerHTML') || $(applyNode.newNodes[patchIndex].vNode).text();
  var newNode = $(applyNode.newNodes[patchIndex].newNode).prop('outerHTML');

  console.log('%c' + originRootNodeHTML.replace(originNode, '%c' + originNode + '%c') + ', %c' + newNode, luffa.ORIGIN_STYLE, luffa.CHANGE_STYLE, luffa.ORIGIN_STYLE, luffa.NEW_STYLE);
}

用Chrome的console来标记修正的部份,及增加的部份。
《基于Virtual DOM与Diff DOM的测试代码天生》
末了,我们好像就能够天生响应的测试代码了。。。

其他

源码见:https://github.com/phodal/luffa
原文:基于Virtual DOM与Diff DOM的测试代码天生

    原文作者:phodal
    原文地址: https://segmentfault.com/a/1190000004172216
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞