Skip to content

Add nextTick to setData calls for Jest tests

Illya Klymov requested to merge xanf-vtu-beta30 into master

What does this MR do?

This MR adds subsequent nextTick to every setData call, if we are making assertions against component template / emitted events of the component

Closes #38108 (closed)

Why do we need this one?

We're preparing to upgrade @vue/test-utils to .beta.30 in &2291 (closed) which will drop sync mode. We want to make sure we're properly handling all the ways of changing component

How do you found all of them 🤔 ?

In order to reveal them I've monkey-patched vue-test-utils to throw if we're trying to access component template after calling setData

let poisoned = false;

const poisonMark = (obj, prop, state) => {
  const orig = obj[prop];
  obj[prop] = function p(...args) {
    const result = orig.apply(this, args);
    poisoned = state;
    return result;
  };
};

const ensureNotPoisoned = (obj, prop) => {
  const orig = obj[prop];
  obj[prop] = function p(...args) {
    if (poisoned) {
      throw new Error(`:${prop}: Dirty tree operation`);
    }
    return orig.apply(this, args);
  };
};

poisonMark(vtu.Wrapper.prototype, 'setData', true);
poisonMark(Vue.prototype, '$nextTick', false);
poisonMark(Vue, 'nextTick', false);
poisonMark(Vue.prototype, '$mount', false);
ensureNotPoisoned(vtu.Wrapper.prototype, 'attributes');
ensureNotPoisoned(vtu.Wrapper.prototype, 'classes');
ensureNotPoisoned(vtu.Wrapper.prototype, 'contains');
ensureNotPoisoned(vtu.Wrapper.prototype, 'emitted');
ensureNotPoisoned(vtu.Wrapper.prototype, 'emittedByOrder');
ensureNotPoisoned(vtu.Wrapper.prototype, 'find');
ensureNotPoisoned(vtu.Wrapper.prototype, 'findAll');
ensureNotPoisoned(vtu.Wrapper.prototype, 'html');
ensureNotPoisoned(vtu.Wrapper.prototype, 'isEmpty');
ensureNotPoisoned(vtu.Wrapper.prototype, 'isVisible');
ensureNotPoisoned(vtu.Wrapper.prototype, 'props');
ensureNotPoisoned(vtu.Wrapper.prototype, 'text');
ensureNotPoisoned(vtu.WrapperArray.prototype, 'at');
ensureNotPoisoned(vtu.WrapperArray.prototype, 'filter');
ensureNotPoisoned(vtu.WrapperArray.prototype, 'isEmpty');
expect.extend({
  toMatchSnapshot: function m(...args) {
    if (poisoned) {
      return {
        pass: false,
        message: () => 'Poisoned tree',
      };
    }
    return toMatchSnapshot.apply(this, args);
  },
});

Why are you fixing test cases which do not fail on upgrade to .beta-30?

Basically, when testing Vue component we do not want to know anything about it's internal structure. "Immediate" reaction to anything - either setData, setProps, trigger, $emit, whatever - is an implementation detail - for example we can perform some action in event handler, or we can update some state, which will trigger watch - former do not require nextTick, latter does. That's why we ensure all such calls are properly wrapped into nextTick

Edited by Illya Klymov

Merge request reports