vuejs / vue-test-utils

Component Test Utils for Vue 2
https://vue-test-utils.vuejs.org
MIT License
3.57k stars 669 forks source link

Tests for default scopedSlots not mounting #706

Closed kayandra closed 6 years ago

kayandra commented 6 years ago

Version

1.0.0-beta.18

Reproduction link

https://codesandbox.io/s/0mrvjmvk6l

Steps to reproduce

Hi there, so I’m building a library for work and I’m using the render function and only accepting default scoped slots. I have the render function currently defined as

  render() {
      return this.$scopedSlots.default({ ...this.computedStateAndHelpers })
  },

When I try to write tests for it (using jest) I always get an error when trying to mount the component

const wrapper = shallowMount(Component, {
    scopedSlots: {
        default: () => '<p></p>'
    }
})
expect(wrapper.isVueInstance()).toBe(true)
expect(wrapper.name()).toBe('component-name')

I also tried

const wrapper = shallowMount(Component, {
    scopedSlots: {
        default: '<p></p>'
    }
})
expect(wrapper.isVueInstance()).toBe(true)
expect(wrapper.name()).toBe('component-name')

The error is

scopedSlots[key].trim is not a function

I don't know what I'm doing wrong, can someone please help me figure this out?

What is expected?

Expecting the test to pass

What is actually happening?

The error is

scopedSlots[key].trim is not a function
donnysim commented 6 years ago

Also having a similar problem (beta 19, 20), scoped slot does not mount.

const wrapper = shallowMount(dataTable, {
      propsData: {
        columns: [
          new Column('id', 'ID').style({ width: '100px' }).expandable(false),
          new Column('title', 'Title'),
          new Column('name', 'Name'),
        ],
        items: [
          { id: 1, title: 'Entry 1', name: 'John' },
        ],
      },
      slots: {
        title: '<span>Test</span>',
      },
      scopedSlots: {
        name: '<span slot-scope="props">{{ props.item.title + props.item.name }}</span>',
      },
    });

    const cells = wrapper.findAll('.og-dt__cell');
    expect(cells.at(0).element.innerHTML).to.equal('1');
    expect(cells.at(1).element.innerHTML).to.equal('<span>Test</span>');
    expect(cells.at(2).element.innerHTML).to.equal('<span>Entry 1John</span>');

check:

expect(cells.at(2).element.innerHTML).to.equal('<span>Entry 1John</span>');

never passess and $scopedSlots is always empty.

kayandra commented 6 years ago

I'm currently using https://github.com/posva/vue-promised as a reference to write tests for scoped slots.

klak-bm commented 6 years ago

I have also a problem with scoped slot but maybe a bit more complex (slot is passed from parent to child). The slot is not applied during test with "mount" whereas it renders on a browser. To reproduce it : git clone https://github.com/klak-bm/scoped-slot-problem && cd scoped-slot-problem/ && npm i && npm test If my scopedSlot problem is really different from your, maybe I can create another issue ?

38elements commented 6 years ago

@eddyerburgh IMHO, It seems that this issue and #657 is same. The content of this issue is easy to understand. I think that keeping this issue and closing #657 is better.

38elements commented 6 years ago

I will send pull request to resolve this issue within 4 days.

aweber1 commented 6 years ago

I was encountering similar scoped slot testing issues when using a functional component or component with a render function. After #808 landed, the final key for me ended up being to ensure that the scoped slot in a test component returns a VNode as that is what render functions ultimately need to return. The following sample code demonstrates returning a VNode from a scoped slot in a test mounted component. Hope someone else finds value in it!

// component

const MyComponent = {
  functional: true,
  props: {
    someComponentProp: { type: String },
  }
  render(createElement, context) {
    const { someComponentProp } = context.props;
    if (context.data.scopedSlots && context.data.scopedSlots.default) {
      // scoped slot should return a VNode
      return context.data.scopedSlots.default(someComponentProp);
    }
    return createElement('div', {}, 'default value if slot isn't defined');
  }
};
// test

const scopedComponent = {
  props: { someProp: { type: String } },
  render(createElement) {
    return createElement('em', {}, this.someProp);
  },
};

const props = { someComponentProp: 'somePropValue' };

const rendered = mount(MyComponent, {
  context: {
    props,
    scopedSlots: {
      default: (someProp) => {
        const scoped = mount(scopedComponent, { propsData: { someProp } });
        // NOTE: `render` function needs VNode
        return scoped.vnode;
      },
    },
  },
});

expect(rendered.html()).toBe(`<em>${props.someComponentProp}</em>`);
38elements commented 6 years ago

I think it was resolved at #893.