Skip to content

Refactoring options api to use vue-router/composables break basic unit tests #3802

Open
@renatodeleao

Description

Version

3.6.5

Reproduction link

stackblitz.com

Steps to reproduce

  1. Giving a basic options API component
    <!-- using options API -->
    <template>
       <div>{{ msg }}</div>
    </template>
    
    <script>
    export default {
      computed: {
          msg() {
             return `My route name is ${this.$route.name}`;
          }
      }
    };
    </script>
  2. refactoring to composition api
    <!-- Refactoring using composition-api -->
    <script>
    import { useRoute } from 'vue-router/composables';
    import { computed } from 'vue';
    
    export default {
      setup() {
        const $route = useRoute();
        const msg = computed(() => {
          `My route name is ${$route.name}`;
        });
    
        return {
          msg,
        };
      },
    };
    </script>
  3. The test that worked before, now breaks.
    import { mount } from '@vue/test-utils';
    import SomeComponent from './SomeComponent.vue';
    
    const $route = {
      path: '/some/path',
    };
    
    function Wrapper() {
      return mount(SomeComponent, {
        mocks: {
          $route,
        },
      });
    }
    
    test('mount component',  () => {
      const wrapper = Wrapper();
      
      expect(wrapper.exists()).toBe(true);
      // TypeError: Cannot read properties of undefined (reading 'currentRoute')
    });

What is expected?

using options/composition-api should not affect the test suite output

What is actually happening?

Using options/composition-api requires different ways to test (mock)


Additional Comments

Link to reproduction with same component written in "options API" mode
https://stackblitz.com/edit/vitejs-vite-rdjvxj?file=src%2Fviews%2FAbout.test.js

Heya! 👋
While I was waiting for the 3.6.x release, I rolled some in-house vue-router hooks as per #3760 (comment). This allowed refactoring some options-API components to composition-api "mode" while keeping my test suite intact.

I thought the refactor to use the official package hooks would be a simple find/replace but my test suite broke. After some digging, the reason is that the hooks implementation relies on router.currentRoute property and afterEach hook, which made the tests using the simplest $route mock as suggested in vue-test-utils docs to fail.

Of course there's several ways to fix it: updating the test mocks as below; setting up a router with localVue as per docs; stubbing modules jest.mock(); etc;

import { mount } from '@vue/test-utils';
import SomeComponent from './SomeComponent.vue';

const $route = {
  path: '/some/path',
};

function Wrapper() {
  return mount(SomeComponent, {
    mocks: {
-      $route,
+      $router: { currentRoute: $route, afterEach: vitest.fn() },
    },
  });
}

Question

If you went this way1, I'm sure there are good reasons for it. I'm just curious on why didn't you go with a similar approach as vue-router@4, as in, with a reactive object derived from getCurrentInstance().proxy.$route as per2 which kept tests working without any change — but surely break some other stuff.


Cheers and thanks for your work on the router! Feel free to close this if you think it's related with @vue/test-utils docs instead ✌️

Footnotes

  1. https://github.com/vuejs/vue-router/blob/e3273b3db905f0d162d57725fba0c459ea5bb463/src/composables/globals.js#L23-L30

  2. https://github.com/vuejs/vue-router/pull/3763/commits/f1eb2b1dba9b5cc5011b230a3121c882d883c4c0#diff-2042e0452c0d0edd730403740acc8d4337382101087ad62a677e1e2fdae05e8fR8-R17

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions