对于 contentEditable 元素,在 "onInput" 上未实现要调用的 Expect 方法

Expect method to have been called is not fulfilled on "onInput" for a contentEditable element

我有一个使用 contentEditable 作为输入法的组件。组件中感兴趣的部分是:

<div className="enter-edit-mode" onClick={view.enterEditMode}>
    <div className="user-input" ref="content" contentEditable onInput={view.textChanged}></div>
</div>

该组件工作正常 - 它根据用户输入进入 textChanged 方法。该方法如下所示:

textChanged: function (e) {
    var view      = this,
        textValue = e.target.innerHTML;

    view.setState({
        enteringText: textValue.length,
        temporaryValue: textValue
    });
}

我遇到的问题 在我尝试测试输入行为时出现。使用 enzymechaisinon 完成设置。我正在使用一个简单的 renderComponent 函数渲染组件,使用酶的 mount 方法。

beforeEach(function () {
    view = renderComponent(card);
    viewReact = view.get(0);
});

it('should enter text changed method on input', function () {
    let spy = sinon.spy(viewReact, 'textChanged');
    view.find('.user-input').simulate('input');
    expect(spy).to.have.been.called;
    spy.restore();
});

它输出 expected textChanged 至少被调用一次,但从未被调用但是,奇怪的部分 是,如果我在组件的方法中放置一个 console.log,它就会到达那里。

我努力让它发挥作用

如果我不模拟输入而是 viewReact.textChanged(),它显然有效。

我猜这是 contentEditable 上的输入法导致的。有什么建议么?如何在 onInput 方法中正确输入文本? (即使在textChanged方法中获取,文本为空)

我做了一点测试,我没有 sinon 但我找到了一个用例:

class Home extends Component {

  state = {
    status: 'default'
  }

  textChanged = () => {
    this.setState({
      status: 'changed'
    })
  }

  render () {
    return (
        <div className="enter-edit-mode" >
          <div className="user-input" ref="content" contentEditable onInput={this.textChanged}>Hello</div>
          <span>{this.state.status}</span>
        </div>
    )
  }
}

和测试

describe('component', () => {
    it('should say changed', () => {
      const component = shallow(<Home />);

      expect(component.find('span').text()).toEqual('default');

      component.find('.user-input').simulate('input', {key: 'a'})

      expect(component.find('span').text()).toEqual('changed');
    });
});

一切如期而至

我可以通过测试以下组件(看起来与您的类似)重现您的问题:

const MyComponent = React.createClass({
  textChanged(e) { console.log('text changed') },
  render() {
    return (
      <div className="enter-edit-mode">
        <div className="user-input" ref="content" contentEditable onInput={ this.textChanged }></div>
      </div>
    );
  }
});

我还设法以一种有点复杂的方式进行了测试:

it('should enter text changed method on input', () => {
  let view = mount(<MyComponent/>);
  let spy  = sinon.spy(view.instance(), 'textChanged');
  view     = view.mount();

  view.find('.user-input').simulate('input');
  expect(spy).to.be.called;
  spy.restore();
});

view.mount()re-mount 组件似乎可以解决这个问题。

我对 Enzyme 一点都不熟悉(虽然我喜欢它:),但看起来它在组件周围添加了各种层,在这种情况下 Sinon 间谍很容易得到 "lost" .

一个可能的警告:我使用 jsdom 在 Node 中完成了所有这些测试,而不是在浏览器中。