首页 > 解决方案 > JQuery 检查笑话测试中的元素可见性

问题描述

我正在尝试在单元测试中检查元素可见性状态是否已更改。我正在.is(":visible")这样做,在单元测试中它总是报告该元素是隐藏的,但它在浏览器中工作正常。

代码就这么简单(参见完整工作示例):

html

<form>
      <div id="field">hi</div>
      <input type='checkbox' id="toggle">toggle</input>
      <input type='checkbox' id="show">show</input>
      <input type='checkbox' id="hide">hide</input>
</form>

javascript

$('form').on('change', 'input#toggle[type="checkbox"]', function() {
  target_row = $('#field');

  if (target_row.length) {
    target_row.toggle("fast");
  }
});

正如我所写,这在浏览器中运行良好,但在以下测试中不起作用:

jest.dontMock('jquery');
jest.dontMock('../toggle.js');

$ = jQuery = require('jquery');

describe('toggle.js', function() {
  var sut = require('../toggle.js');
  function givenFields(formFields) {
    document.documentElement.innerHTML = `
      <html><body><form>
      <div id="field">hi</div>
      ${formFields}
      </form></body></html>
    `;
    sut.initialize();
  }

  it('toggles the field', function() {
    givenFields(`<input type='checkbox' id="toggle">toggle</input>`);

    var initiallyVisible = $("#field").is(":visible");

    $('#toggle').click();

    // Checking that this is not jsdom rendering issue
    expect(document.hidden).toEqual(false);

    expect($("#field").is(":visible")).toEqual(!initiallyVisible);
  });

  it('shows the field', function() {
    givenFields(`<input type='checkbox' id="show">show</input>`);

    $('#show').click();

    expect($("#field").is(":visible")).toEqual(true);
  });
});

结果是:

  ● toggle.js › toggles the field

    expect(received).toEqual(expected)

    Expected: true
    Received: false

      26 |     expect(document.hidden).toEqual(false);
      27 |
    > 28 |     expect($("#field").is(":visible")).toEqual(!initiallyVisible);
         |                                        ^
      29 |   });
      30 |
      31 |   it('shows the field', function() {

      at Object.toEqual (src/__test__/toggle.spec.js:28:40)

  ● toggle.js › shows the field

    expect(received).toEqual(expected)

    Expected: true
    Received: false

      34 |     $('#show').click();
      35 |
    > 36 |     expect($("#field").is(":visible")).toEqual(true);
         |                                        ^
      37 |   });
      38 | });
      39 |

      at Object.toEqual (src/__test__/toggle.spec.js:36:40)

package.json是:

{
  "name": "toggle",
  "devDependencies": {
    "jasmine": "3.2.0",
    "jest-cli": "24.1.0",
    "jquery": "3.3.1"
  },
  "scripts": {
    "test": "jest"
  }
}

任何想法可能有什么问题,我该如何解决这个问题?

我不太喜欢的一个选项,因为它不是一个解决方案,但一种解决方法是我可以检查与 DOM 的交互而不是它的状态。我的意思是我可以验证对函数的调用toggleshowhide。这种方法有一个缺点,即它增加了复杂性,因为这需要我在测试中维护 DOM 元素的状态并引入关于初始状态的假设。

标签: javascriptjquerynode.jsjestjs

解决方案


我认为第一个问题是持续时间(在本例中为“快速”),切换和显示功能不仅会改变可见性:

当提供持续时间、普通对象或单个“完整”函数时,.toggle() 成为动画方法。.toggle() 方法同时为匹配元素的宽度、高度和不透明度设置动画。当这些属性在隐藏动画后达到 0 时,显示样式属性设置为 none,以确保元素不再影响页面的布局。

所以你实际上想要检查display元素的。

下一个问题是 jquery 将显示设置为 200 毫秒none,因此我们需要考虑这一点,这意味着我们可以编写如下内容:

  it('toggles the field', function(done) {
    givenFields(`
      <div id="field">kuku</div>
      <input type='checkbox' id="toggle">toggle</input>
    `);

    var initialDisplay = $("#field").css('display');

    $('#toggle').click();

    // Checking that this is not jsdom rendering issue
    expect(document.hidden).toEqual(false);
    var expectedDisplay = initialDisplay === 'block' ? 'none' : 'block'
    setTimeout(function() {
      expect($("#field").css('display')).toEqual(expectedDisplay);
      done()
    }, 200)
  });

可能还有一种方法可以将jest timer-mocks注入到 jquery 中,这样您就可以避免 200 毫秒的等待。会很酷,但不确定是否可能。

编辑: jQuery.fx.off = true; 这很好用,给我们留下:

jest.dontMock('jquery');
jest.dontMock('./toggle.js');

$ = jQuery = require('jquery');
jQuery.fx.off = true;

describe('toggle.js', function() {
  var sut = require('./toggle.js');
  function givenFields(formFields) {
    document.documentElement.innerHTML = `
      <html><body><form>${formFields}</form></body></html>
    `;
    sut.initialize();
  }

  it('toggles the field', function() {
    givenFields(`
      <div id="field">kuku</div>
      <input type='checkbox' id="toggle">toggle</input>
    `);

    var initialDisplay = $("#field").css('display');

    $('#toggle').click();

    // Checking that this is not jsdom rendering issue
    expect(document.hidden).toEqual(false);
    var expectedDisplay = initialDisplay === 'block' ? 'none' : 'block'
    expect($("#field").css('display')).toEqual(expectedDisplay);
  });

  it('shows the field', function() {
    givenFields(`
      <div id="field">kuku</div>
      <input type='checkbox' id="show">show</input>
    `);

    $('#show').click();

    expect($("#field").is(":visible")).toEqual(true);
  });

});

推荐阅读