Skip to content

Ensure nextTick is called after setProps

Illya Klymov requested to merge xanf-vtu-30-set-props into master

What does this MR do?

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

Closes #38110 (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 setProps

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, 'setProps', 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

Does this MR meet the acceptance criteria?

Conformity

Edited by Illya Klymov

Merge request reports