1

I testing Vuetify navigation drawer component because I must change it a little bit and I have very weird problem. Additionally I add that I use TypeScript

TypeError: Cannot read property 'querySelector' of undefined

  28 | 
  29 |   private setBorderWidth(): void {
> 30 |     const border = this.drawer.$el.querySelector(
     | ^
  31 |       ".v-navigation-drawer__border"
  32 |     );
  33 | 

  at VueComponent.setBorderWidth (src/components/Navigation/NavigationDrawer.vue:30:1)
  at VueComponent.mounted (src/components/Navigation/NavigationDrawer.vue:90:1)
  at invokeWithErrorHandling (node_modules/vue/dist/vue.runtime.common.dev.js:1850:57)
  at callHook (node_modules/vue/dist/vue.runtime.common.dev.js:4207:7)
  at Object.insert (node_modules/vue/dist/vue.runtime.common.dev.js:3133:7)
  at invokeInsertHook (node_modules/vue/dist/vue.runtime.common.dev.js:6326:28)
  at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6543:5)
  at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3933:19)
  at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4054:10)
  at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4465:25)
  at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4454:12)
  at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4061:3)
  at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8392:10)
  at mount (node_modules/@vue/test-utils/dist/vue-test-utils.js:13855:21)
  at shallowMount (node_modules/@vue/test-utils/dist/vue-test-utils.js:13881:10)
  at Object.<anonymous> (tests/unit/components/Navigation/NavigationDrawer.spec.ts:20:21)

any suggestions whats is wrong bellow my test code I am open to all advicess

import { shallowMount, createLocalVue } from "@vue/test-utils";
import Vuetify from "vuetify";
import NavigationDrawer from "@/components/Navigation/NavigationDrawer.vue";

const localVue = createLocalVue();

describe("NavigationDrawer.vue", () => {
  let vuetify: any;

  beforeEach(() => {
    vuetify = new Vuetify();
  });

  test("Render props correctly when passed", () => {
    const props = {
      width: 300,
      visible: true
    };

    const wrapper = shallowMount(NavigationDrawer, {
      localVue,
      vuetify,
      propsData: props,
    });

    expect(wrapper.props()).toStrictEqual(props);
    expect(wrapper.vm.$data.navigation.show).toBeTruthy();
  });
});

Navigation drawer component I added possibility to resize this drawer

<template>
  <v-navigation-drawer
    ref="drawer"
    v-bind="$attrs"
    :width="navigation.width"
    v-model="navigation.visible"
  >
    <slot></slot>
  </v-navigation-drawer>
</template>

<script lang="ts">
import { Component, Vue, Prop, Ref } from "vue-property-decorator";

@Component
export default class NavigationDrawer extends Vue {
  @Prop({ required: true, type: [Number], default: 256 })
  private width!: number;
  @Prop({ required: true, type: Boolean, default: true })
  private visible!: boolean;

  @Ref("drawer") private drawer!: any;

  private navigation = {
    show: this.visible,
    width: this.width,
  };

  private setBorderWidth(): void {
    const border = this.drawer.$el.querySelector(
      ".v-navigation-drawer__border"
    );

    border.style.width = "5px";
    border.style.cursor = "col-resize";
  }

  private setEvents(): void {
    const minSize = 5;
    const el: HTMLElement = this.drawer.$el;

    const border = el.querySelector(
      ".v-navigation-drawer__border"
    )! as HTMLElement;
    const direction: string = el.classList.contains(
      "v-navigation-drawer--right"
    )
      ? "right"
      : "left";

    function resize(e: MouseEvent) {
      let clientX: number =
        direction === "right"
          ? document.body.scrollWidth - e.clientX
          : e.clientX;

      if (clientX <= 5) {
        clientX = 5;
      }

      el.style.width = `${clientX}px`;
    }

    border.addEventListener(
      "mousedown",
      (e: MouseEvent) => {
        if (e.offsetX < minSize) {
          el.style.transition = "initial";
          document.body.style.userSelect = "none";
          document.addEventListener("mousemove", resize, false);
        }
      },
      false
    );

    document.addEventListener(
      "mouseup",
      () => {
        el.style.transition = "";
        this.navigation.width = Number(el.style.width.replace("px", ""));
        document.body.style.cursor = "";
        document.body.style.userSelect = "";
        document.removeEventListener("mousemove", resize, false);
      },
      false
    );
  }

  private mounted() {
    this.setBorderWidth();
    this.setEvents();
  }
}
</script>

1 Answer 1

1

Replace shallowMount with mount and it should be ok.

Remember that vuetify components needs to be mounted to to access them.

EDIT:

It seems that you have invalid v-model directive value. I suppose it should be navigation.show instead of navigation.visible

EDIT 2: Try calling localVue.use(Vuetify) just right after const localVue = createLocalVue()

It could result in Multiple instances of Vue detected. Then i would suggest to do it like so:

...
import Vue from 'vue'
import Vuetify from 'vuetify'
... 

Vue.use(Vuetify);

...

const wrapper = mount(NavigationDrawer, {
  vuetify,
  propsData: props,
});

EDIT 3 (Added my code):

import { createLocalVue, mount } from "@vue/test-utils";

import Vuetify from "vuetify";
import NavigationDrawer from "@/components/NavigationDrawer.vue";

const localVue = createLocalVue()
localVue.use(Vuetify)

describe("NavigationDrawer.vue", () => {
  let vuetify: any;

  beforeEach(() => {
    vuetify = new Vuetify();
  });

  test("Render props correctly when passed", () => {
    const props = {
      width: 300,
      visible: true
    };

    const wrapper = mount(NavigationDrawer, {
      localVue,
      vuetify,
      propsData: props,
    });

    expect(wrapper.props()).toStrictEqual(props);
    expect(wrapper.vm.$data.navigation.show).toBeTruthy();
  });
});
import { createLocalVue, mount } from "@vue/test-utils";
import Vue from 'vue'
import Vuetify from "vuetify";
import NavigationDrawer from "@/components/NavigationDrawer.vue";

Vue.use(Vuetify)

describe("NavigationDrawer.vue", () => {
  let vuetify: any;

  beforeEach(() => {
    vuetify = new Vuetify();
  });

  test("Render props correctly when passed", () => {
    const props = {
      width: 300,
      visible: true
    };

    const wrapper = mount(NavigationDrawer, {
      vuetify,
      propsData: props,
    });

    expect(wrapper.props()).toStrictEqual(props);
    expect(wrapper.vm.$data.navigation.show).toBeTruthy();
  });
});

Sign up to request clarification or add additional context in comments.

7 Comments

I think the same but with mount it's the same problem
Can you add some code for navigationDrawer.vue? I'd like to reproduce it on my environment
Yes that true thanks :D but this wan't solve my problem :P
Weird, that works for me, but I've updated my answer with another possible solution ;-)
Yeah that is very weird because if I added localVue.use(Vuetify) after const localVue = createLocalVue() now I got error that border is null. For you this test passed ? Could you share your code for me ?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.