【问题标题】:Is it possible to create DOM for Knockout to bind to?是否可以为 Knockout 创建 DOM 以绑定到?
【发布时间】:2017-06-01 07:55:52
【问题描述】:

我们正在尝试弄清楚如何将Jest.jsKnockout.js 结合使用。本质上,我们想要创建一个小的 DOM 片段,例如:

<div class="card" data-bind="component: myCustomComponent"/>

然后可以使用自定义视图模型调用ko.applyBindings(),并使用该 DOM 片段并让 Knockout 将组件加载到视图模型上,这样我们就可以进行 Jest 快照测试。

我们正在努力解决的问题是,我如何有效地创建一个适合 ko.applyBindings() 的 DOM 片段,因为它需要一个我们不确定如何创建/选择的 DOM 节点。

编辑

我已经尝试过@user3297291 的答案,并整理了以下示例,不幸的是我无法开始工作。这是我整理的示例:

const jsdom = require("jsdom");
const ko = require("knockout");

describe("Knockout Test", () => {

    beforeAll(() => {
        ko.components.register("greeting", {
            viewModel: function(params) {
                //params.name = "test";
                //this.message = ko.observable(`Hello ${params.name}`);
                this.message = ko.observable("Hello World!");
            },
            template: '<span data-bind="text: message"/>'
        });
    });

    it("test pass", (done) => {

        jsdom.env(`<div class="wrapper"></div>`, [], (err, window) => {
            var wrapper = window.document.querySelector(".wrapper");
            var element = window.document.createElement("div");

            element.setAttribute("data-bind", 'component: "greeting"');
            wrapper.appendChild(element);

            ko.applyBindings({ name: 'Ian' }, wrapper);

            setTimeout(() => {
                console.log(element.innerHTML);
                expect(element.innerHTML).toMatchSnapshot();
                done();
            }, 10);

          }
        );
    });
});

如果我将element.setAttribute("data-bind", 'component: "greeting"'); 修改为element.setAttribute("data-bind", 'text: name');,那么它可以正常工作,但是加载一个组件(这是我真正想要做的)似乎总是导致一个空的element.innerHTML

如果你想重现,这里有一个 package.json,我正在运行npm run jest

{
  "name": "kotest",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "jest": "jest"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "jasmine": "^2.5.3",
    "jest": "^18.1.0",
    "jsdom": "^9.9.1",
    "knockout": "^3.4.1"
  }
}

【问题讨论】:

  • 您可以使用 jQuery 或 vanilla js 和 pass it as the second argument to applyBindings AFAIK 创建一个 DOM 元素。你试过了吗?
  • @Jeroen 你确定你可以在 nodejs/jest 环境中做到这一点吗?我已经尝试过了,似乎 jQuery 无法真正创建 DOM 元素,它会返回一个包含空对象的数组。这反过来又可以防止敲除绑定
  • 不,不完全确定。我对 jestjs 了解不多,也没有从您的问题中了解您正在使用 nodejs 环境。过去,我通常使用 Chutzpah 对 KnockoutJS 代码运行 QUnit 测试,AFAIK 在后台使用 PhantomJS 为您提供 DOM 东西。希望其他人可以提供帮助。祝你好运!
  • @Jeroen 啊,很公平。是的,不幸的是我相信 Jest 只在节点环境中运行。也许我会在我的问题中添加一个额外的标签。
  • 我已经使用jsdom 在我的答案中尝试了一些代码来模仿window/DOM,并且没有遇到任何问题。很想知道它是否有任何帮助......

标签: javascript node.js knockout.js jestjs


【解决方案1】:

在浏览器的 javascript 环境中,您可以通过代码创建 DOM 元素来虚拟测试淘汰赛。例如,您可以这样做:

ko.applyBindings({}, document.createElement("div"));

这意味着您可以通过将组件绑定应用到虚拟 &lt;div&gt; 并检查其内容来编写任何 UI 测试。下面的示例显示了一些基础知识。

function testComponent(options, test) {
  var wrapper = document.createElement("div");

  // If you have a HTML string, you can do this by setting
  // wrapper.innerHTML = "<div ... ></div>";
  var binding = "component: { name: name, params: params }"
  wrapper.setAttribute("data-bind", binding);
  
  // Apply bindings to virtual element
  ko.applyBindings(options, wrapper);
  
  // Once done, call test with wrapper
  setTimeout(test.bind(wrapper));
};

var messageEditorTest = function() {
  var opts = { name: "message-editor", params: {} };
  
  testComponent(opts, function() {
    var input = this.querySelector("input"),
        display = this.querySelector("span"),
        vm = ko.dataFor(this.firstElementChild);
    
    console.log("MESSAGE EDITOR TESTS:");
    
    console.log("Has an input field:", 
                input !== null);
    
    console.log("Has a display field:",
                display !== null);
    
    console.log("Length is initially zero:",
                display.innerText === "0");
    
    vm.text("Four");
    console.log("Length reflects value input:",
                display.innerText === "4");
                
  });
};

// Component
ko.components.register('message-editor', {
    viewModel: function(params) {
        this.text = ko.observable(params && params.initialText || '');
    },
    template: 'Message: <input data-bind="value: text" /> '
            + '(length: <span data-bind="text: text().length"></span>)'
});

messageEditorTest();
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"&gt;&lt;/script&gt;

现在,如果您想在没有浏览器帮助的情况下在 Node.js 中进行测试,我不确定它是如何工作的……我认为您可以使用像 @ 这样的 javascript DOM 实现987654321@,或者您可以尝试启动“测试浏览器”,例如PhantomJS

编辑:我使用 jsdom 进行了快速测试,一切似乎都正常

  • 运行npm install jsdom
  • 运行npm install knockout
  • 创建一个名为test.js的文件:

    var jsdom = require("jsdom");
    var ko = require("knockout");
    
    jsdom.env(
      '<div class="wrapper"></div>',
      [],
      function (err, window) {
        var wrapper = window.document.querySelector(".wrapper");
    
        var element = window.document.createElement("div");
        element.setAttribute("data-bind", "text: test");
        wrapper.appendChild(element);
    
        ko.applyBindings({ test: 'My Test' }, wrapper);
    
        console.log(element.innerHTML); // Logs "My Test"
      }
    );
    
  • 运行node test.js
  • 日志My Test

【讨论】:

  • 进一步研究(尝试加载组件)我得到以下问题:[Error]: Message: First argument to Node.prototype.insertBefore must be a Node​​ ​​​​​[Info]: Thu, 19 Jan 2017 09:45:58 GMT wallaby:workers Test executed: component registered​​​​​ ​​[Error]: at HTMLDivElementImpl.insertBefore (C:\source\mood\kotest\node_modules\jsdom\lib\jsdom\living\nodes\Node-impl.js:171:13)​​
  • 我没有得到相同的错误代码,但我也不确定innerHTML 是否已设置。如果我能找到时间,我今天晚些时候再看看。
  • 我能够通过为组件设置synchronous: true 来重现您的错误。问题似乎与默认模板引擎与我们的虚拟环境不能很好地协同工作有关...我能够通过覆盖默认的loadTemplate 方法来解决这个问题。在此处查看测试:jsfiddle.net/sygjajcj 我建议您在调用applyBindings 的实际测试之前再看看并创建更多测试。例如“注册组件”expect(ko.components.isRegistered("greeting")).toBeTruthy();
  • 我可能遗漏了一些东西,如果你有机会可以看看我尝试重现你的修复吗? github.com/MooDEdge/ko-jest
  • 我认为问题可能是 beforeAll 没有使用我们用来运行测试的相同 window 属性运行......但仍然不完全确定。通过这些修改,我设法通过了您的测试:jsfiddle.net/f6y848bd(参见 cmets)
猜你喜欢
  • 2016-05-09
  • 1970-01-01
  • 1970-01-01
  • 2011-08-04
  • 2022-11-08
  • 2016-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多