Search code examples
vue.jsvuejs2jestjsvue-routervue-test-utils

How to test the access of protected route guard with in Vuejs?


I implemented a route guard to protect the /settings route with the vue-router method beforeEnter().

I try to test that the route is protected to admins only.

I am using Vuejs 2, Vue-router, Vuex and vue-test-utils.

router.js

import Vue from 'vue';
import Router from 'vue-router';

Vue.use(Router);

export default new Router({
  routes: [
    ..., // other routes
    {
      path: '/settings',
      name: 'Settings',
      component: () => import('./views/settings'),
      beforeEnter: (to, from, next) => {
        next(store.state.isAdmin);
      }
    }
  ]
});

the unit test:

  test('navigates to /settings view if the user is admin', () => {
    const localVue = createLocalVue();
    localVue.use(Vuex);
    localVue.use(VueRouter);
    const router = new VueRouter();

    const wrapper = shallowMount(App, {
      stubs: ['router-link', 'router-view'],
      localVue,
      mocks: {
        $store: store
      },
      router
    });
    wrapper.vm.$route.push({ path: '/settings' });
    // test if route is set correctly
  });

current logs output:

wrapper.vm.$route` is undefined.

How can I mount the App correctly and access the router? How can I test the current route to verify that the admin user has been redirected succesfully?


Solution

  • Thank logan for the link. It seems like the best possible solution:

    As of now there is no easy way to test navigation guards. If you want to simulate the event triggering by calling router.push function, you are going to have a hard time. A better easier solution is to call the guard manually in beforeEach(), but even this solution doesn't have a clean approach. See the following example:

    beforeRouteEnter

    // my-view.js
    class MyView extends Vue {
      beforeRouteEnter (to, from, next) {
        next(function (vm) {
          vm.entered = true;
        });
      }
    }
    // my-view.spec.js
    it('should trigger beforeRouteEnter event', function () {
      const view = mount(MyView);
      const spy = sinon.spy(view.vm.$options.beforeRouteEnter, '0'); // you can't just call view.vm.beforeRouteEnter(). The function exists only in $options object.
    
      const from = {}; // mock 'from' route
      const to = {}; // mock 'to' route
      view.vm.$options.beforeRouteEnter[0](to, from, cb => cb(view.vm));
    
      expect(view.vm.entered).to.be.true;
      expect(spy).to.have.been.called;
    });