#How to mock multiple refs (and refs in general) in Nuxt?

1 messages · Page 1 of 1 (latest)

teal ridgeBOT
#

Hi @visual ferry. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

To mock multiple refs and test your SideBar.vue component in Nuxt using Vitest and Nuxt test utils, you can follow these steps:

  1. First, you'll need to mock the useRoute composable to control the path ref. You can do this using mockNuxtImport:
import { mockNuxtImport } from '@nuxt/test-utils/runtime'

mockNuxtImport('useRoute', () => {
  return () => ({
    path: '/mocked-path'
  })
})
  1. For mocking the other refs (isCollapsed and tokens), you can use the ref function from Vue and set their initial values in your test setup. Here's how you can structure your test:
import { describe, it, expect } from 'vitest'
import { ref } from 'vue'
import { mountSuspended } from '@nuxt/test-utils/runtime'
import SideBar from './SideBar.vue'

describe('SideBar', async () => {
  it('toggles isCollapsed and displays tokens', async () => {
    const wrapper = await mountSuspended(SideBar, {
      setup() {
        return {
          isCollapsed: ref(false),
          tokens: ref(100)
        }
      }
    })

    // Test if tokens are displayed
    expect(wrapper.text()).toContain('100')

    // Find and click the toggle button
    const toggleButton = wrapper.find('button.toggle-collapse')
    await toggleButton.trigger('click')

    // Check if isCollapsed is toggled
    expect(wrapper.vm.isCollapsed).toBe(true)
  })
})

This approach uses the mountSuspended function from @nuxt/test-utils/runtime to mount your component within the Nuxt environment Nuxt Docs: mountSuspended (https://nuxt.com/docs/getting-started/testing#mountsuspended). The setup option allows you to provide initial values for your refs.

Remember to adjust the selectors (like button.toggle-collapse) to match your actual component structure.

#
  1. If you need to interact with or assert against the ref values directly, you can access them through wrapper.vm:
expect(wrapper.vm.isCollapsed).toBe(false)
expect(wrapper.vm.tokens).toBe(100)

This approach should allow you to test both the toggling of isCollapsed and the display of tokens in your SideBar component.

Note that this solution assumes you're using the Nuxt testing environment. Make sure your test file is named with the .nuxt.spec.ts extension or includes the // @vitest-environment nuxt comment at the top of the file to ensure the Nuxt environment is used Nuxt Docs: Using a Nuxt Runtime Environment (https://nuxt.com/docs/getting-started/testing#using-a-nuxt-runtime-environment).

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@visual ferry Kapa.ai is still learning and improving, please let me know how I did by reacting below.

visual ferry
#

it('should render the sidebar', async () => {
const wrapper = await mountSuspended(SideBar, {
setup() {
return {
tokens: 10000,
};
},
});
await wrapper.setData({ tokens: 10000 });

    expect(wrapper.html()).toContain('Home');
    expect(wrapper.html()).toContain('Suggestions');
    expect(wrapper.html()).toContain('Settings');
    expect(wrapper.html()).toContain('10.0k Tokens');
});

⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
TypeError: Cannot destructure property 'expose' of 'undefined' as it is undefined.
❯ setup components/dashboard/SideBar.vue:138:15
136| </template>
137|
138| <script setup>
| ^
139| import { ref } from 'vue';
140| import {

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

@teal ridge

teal ridgeBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The error you're encountering suggests that there's an issue with how the component is being mounted or how the setup function is being handled. Let's address this problem step by step:

  1. First, it's important to note that when using <script setup>, you don't need to explicitly return values from the setup function. The setup() option in mountSuspended is not the correct way to provide props or refs to a component using <script setup>.

  2. Instead of using setup(), you should use the props option to pass props to the component. Here's how you can modify your test:

it('should render the sidebar', async () => {
    const wrapper = await mountSuspended(SideBar, {
        props: {
            tokens: 10000,
        },
    });

    expect(wrapper.html()).toContain('Home');
    expect(wrapper.html()).toContain('Suggestions');
    expect(wrapper.html()).toContain('Settings');
    expect(wrapper.html()).toContain('10.0k Tokens');
});
  1. Remove the await wrapper.setData({ tokens: 10000 }); line. When using <script setup>, you typically don't use setData to modify component state. The component should react to prop changes automatically.

  2. Make sure your SideBar component is properly defining the tokens prop. In your <script setup>, you should have something like this:

<script setup>
import { defineProps } from 'vue'

const props = defineProps({
  tokens: {
    type: Number,
    required: true
  }
})
</script>
  1. If you're using TypeScript, you might define props like this:
<script setup lang="ts">
const props = defineProps<{
  tokens: number
}>()
</script>
#
  1. If you're still encountering issues, it might be related to how your component is structured or how the Nuxt testing environment is set up. Make sure you're using the latest version of @nuxt/test-utils and that your test file is properly configured to use the Nuxt environment Nuxt Docs: Using a Nuxt Runtime Environment (https://nuxt.com/docs/getting-started/testing#using-a-nuxt-runtime-environment).

  2. If the problem persists, you might want to try using renderSuspended instead of mountSuspended. This function is designed to work with @testing-library/vue and might provide a different approach to testing your component Nuxt Docs: renderSuspended (https://nuxt.com/docs/getting-started/testing#rendersuspended).

Remember, when using <script setup>, the context of the component is handled differently compared to the options API, which is why the setup() function in the test was causing issues. By passing props directly and ensuring your component is correctly defining those props, you should be able to resolve this error.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@visual ferry Kapa.ai is still learning and improving, please let me know how I did by reacting below.

visual ferry
#

<script setup lang="ts">
import { ref } from 'vue';
import {
ChevronLeft,
ChevronRight,
Search,
Home,
FileText,
Settings,
User,
Menu,
X,
BarChart2,
Bot,
LogOut,
HandCoinsIcon,
} from 'lucide-vue-next';

const isCollapsed = ref(false);
const tokens = ref(10030000);
const path = ref(useRoute().path);

const toggleSidebar = () => {
isCollapsed.value = !isCollapsed.value;
console.log(path.value);
};

const navItems = [
{
name: 'Home',
icon: Home,
link: '/dashboard',
paths: [/dashboard/i, /dashboard/daytistics/i, /dashboard/daytistics/\d+/i],
},
{
name: 'Suggestions',
icon: Bot,
link: '/dashboard/suggestions',
paths: [/dashboard/suggestions/],
},
{
name: 'Settings',
icon: Settings,
link: '/account/settings',
paths: [/account/settings/],
},
];
</script>

@teal ridge

Does this help to identify the issue

teal ridgeBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Thank you for providing the component code. This additional context helps to identify potential issues. Based on the code you've shared, I can see that you're using <script setup> with TypeScript, which is good. However, there are a few things to note that might be causing the issue you're experiencing:

  1. The useRoute() composable is being used directly in the setup scope. This can cause issues in testing because Nuxt composables require a specific context to run. To address this, you can wrap it in a computed property:
import { computed } from 'vue'

const route = useRoute()
const path = computed(() => route.path)
  1. The tokens ref is defined within the component, but in your test, you're trying to pass it as a prop. Instead, you should define it as a prop if you want to control it from outside the component:
const props = defineProps<{
  initialTokens?: number
}>()

const tokens = ref(props.initialTokens ?? 10030000)
  1. To make the isCollapsed state and toggleSidebar function accessible for testing, you need to expose them:
import { ref } from 'vue'

const isCollapsed = ref(false)
const toggleSidebar = () => {
  isCollapsed.value = !isCollapsed.value
  console.log(path.value)
}

defineExpose({ isCollapsed, toggleSidebar })

Now, you can modify your test to account for these changes:

import { describe, it, expect } from 'vitest'
import { mountSuspended } from '@nuxt/test-utils/runtime'
import SideBar from './SideBar.vue'

describe('SideBar', async () => {
  it('should render the sidebar and toggle collapse', async () => {
    const wrapper = await mountSuspended(SideBar, {
      props: {
        initialTokens: 10000000
      }
    })
#

expect(wrapper.html()).toContain('Home')
expect(wrapper.html()).toContain('Suggestions')
expect(wrapper.html()).toContain('Settings')
expect(wrapper.html()).toContain('10.0M Tokens')

expect(wrapper.vm.isCollapsed).toBe(false)
await wrapper.vm.toggleSidebar()
expect(wrapper.vm.isCollapsed).toBe(true)

})
})


Make sure you've mocked the `useRoute` composable as mentioned in the previous answer:

```typescript
import { mockNuxtImport } from '@nuxt/test-utils/runtime'

mockNuxtImport('useRoute', () => {
  return () => ({
    path: '/dashboard'
  })
})

These changes should help resolve the issues you're facing with testing this component. The key points are:

  1. Use computed for values derived from Nuxt composables.
  2. Define props for values you want to control from outside.
  3. Use defineExpose to make internal state and methods available for testing.
  4. Mock Nuxt composables like useRoute in your test setup.

Nuxt Docs: Auto-imports (https://nuxt.com/docs/guide/concepts/auto-imports) provides more information on how Nuxt handles imports and composables, which is crucial for understanding how to properly structure and test components in a Nuxt environment.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: