10

THE SITUATION

I am trying to shallowMount a component, unsuccessfully.

The component makes use of $refs to read the height of a div. That value is read inside a computed property. Then in the mounted lifecycle I save that value in the store.

The logic itself is simple and works fine. But in the test suite, the mounting of the component breaks, because the $refs key is undefined.

To be clear: I don't intend to test the $refs, I just need to mount the component and move on doing actual unit-testing.

THE COMPONENT

This is the markup:

<div ref="tgmp">

I save the height of the div in a computed property:

computed: {
  barH() {
    return this.$refs.tgmp.clientHeight
  }
}

And then, in the mounted lifecycle, I commit the value in the store:

this.$store.commit('setBarHeight', this.barH)

THE TEST

This is the test. I have omitted irrelevant stuff, like installing the store in the localVue.

beforeEach(() => {
  wrapper = shallowMount(Bar, {
    store,
  })
})

test('is a Vue instance', () => {
  expect(wrapper.isVueInstance()).toBeTruthy()
})

THE ERROR

Error in mounted hook: "TypeError: Cannot read property 'clientHeight' of undefined"

TypeError: Cannot read property 'clientHeight' of undefined

ATTEMPT

I have been trying searching anywhere for a solution, but couldn't find it. I have tried to mock the $refs, but without success:

wrapper = shallowMount(ThePlayerBar, {
  store,
  mocks: {
    $refs: {
      tgmp: {
        clientHeight: 600
      }
    }
  }
})

THE QUESTION

How can I mount a component that makes us of $refs in the mounted lifecycle?

skyboyer
  • 22,209
  • 7
  • 57
  • 64
FrancescoMussi
  • 20,760
  • 39
  • 126
  • 178
  • I'm not sure what's going on with refs in shallowMount, but you can try `mount` with specific stubs or mock a computed. – Estus Flask Apr 15 '20 at 09:42
  • @EstusFlask thanks for replying! It seems it's working. I will make some more stuff to be sure of it. If you want you can reply since it seems the correct solution. – FrancescoMussi Apr 15 '20 at 10:06
  • I checked it and I'd expect a ref to be there in your case. Probably `
    ` doesn't exist when you render it, could be if it's a child of other component or affected by a directive.
    – Estus Flask Apr 15 '20 at 11:12

1 Answers1

9

shallowMount is supposed to provide refs, so this.$refs.tgmp should be <div> element in case <div ref="tgmp"> exists in the view on initial render.

$refs isn't supposed to be mocked because it's internal property and assigned on component initialization. It's computed property that relies on a ref, so it can be mocked if necessary because element height is expected to be 0 in JSDOM:

jest.spyOn(ThePlayerBar.options.computed, 'barH').mockReturnValue(600);

Or:

  wrapper = shallowMount(Bar, {
    store,
    computed: { barH: () => 600 }
  })
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Thanks for the answer. That spy doesn't seem to work. I get errors like: Cannot read property 'computed' of undefined. Property barH does not have access type get. But I managed to make it working by stubbing the computed property as you suggested in the comment: `computed: { barH() { return 60 }}` That spyOn did you tested in this condition? In case you can edit the answer or I can add that computed stub as an edit to my question. – FrancescoMussi Apr 15 '20 at 11:53
  • I updated the answer with both ways. I'm not sure under which condition a spy could fail because components have `options` exposed and it worked for me before, but I fixed it to be `jest.spyOn` with no `get`. – Estus Flask Apr 15 '20 at 15:47