#How to mock multiple refs (and refs in general) in Nuxt?
1 messages · Page 1 of 1 (latest)
To mock multiple refs and test your SideBar.vue component in Nuxt using Vitest and Nuxt test utils, you can follow these steps:
- First, you'll need to mock the
useRoutecomposable to control thepathref. You can do this usingmockNuxtImport:
import { mockNuxtImport } from '@nuxt/test-utils/runtime'
mockNuxtImport('useRoute', () => {
return () => ({
path: '/mocked-path'
})
})
- For mocking the other refs (
isCollapsedandtokens), you can use thereffunction 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.
- 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.
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
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:
-
First, it's important to note that when using
<script setup>, you don't need to explicitly return values from the setup function. Thesetup()option inmountSuspendedis not the correct way to provide props or refs to a component using<script setup>. -
Instead of using
setup(), you should use thepropsoption 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');
});
-
Remove the
await wrapper.setData({ tokens: 10000 });line. When using<script setup>, you typically don't usesetDatato modify component state. The component should react to prop changes automatically. -
Make sure your SideBar component is properly defining the
tokensprop. 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>
- If you're using TypeScript, you might define props like this:
<script setup lang="ts">
const props = defineProps<{
tokens: number
}>()
</script>
-
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-utilsand 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). -
If the problem persists, you might want to try using
renderSuspendedinstead ofmountSuspended. This function is designed to work with@testing-library/vueand 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.
<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
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:
- 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 acomputedproperty:
import { computed } from 'vue'
const route = useRoute()
const path = computed(() => route.path)
- The
tokensref 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)
- To make the
isCollapsedstate andtoggleSidebarfunction 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:
- Use
computedfor values derived from Nuxt composables. - Define props for values you want to control from outside.
- Use
defineExposeto make internal state and methods available for testing. - Mock Nuxt composables like
useRoutein your test setup.