UNPKG

agent-rules-kit

Version:

Bootstrap of **Cursor** rules (`.mdc`) and mirror documentation (`.md`) for AI agent-guided projects.

637 lines (524 loc) 13.5 kB
--- description: Testing implementation examples for Nuxt 2 globs: <root>/test/**/*.spec.js,<root>/test/**/*.spec.ts,<root>/test/**/*.test.js,<root>/test/**/*.test.ts alwaysApply: false --- # Nuxt 2 Testing Implementation This document provides specific code examples for implementing tests in Nuxt 2 applications in {projectPath}. ## Setting Up Testing Environment ### Jest with Nuxt 2 Configure Jest in your Nuxt 2 project: ```js // jest.config.js module.exports = { moduleNameMapper: { '^@/(.*)$': '<rootDir>/$1', '^~/(.*)$': '<rootDir>/$1', '^vue$': 'vue/dist/vue.common.js', }, moduleFileExtensions: ['js', 'vue', 'json'], transform: { '^.+\\.js$': 'babel-jest', '.*\\.(vue)$': 'vue-jest', }, collectCoverage: true, collectCoverageFrom: [ '<rootDir>/components/**/*.vue', '<rootDir>/pages/**/*.vue', '<rootDir>/store/**/*.js', ], testEnvironment: 'jsdom', }; ``` Add necessary scripts to your package.json: ```json { "scripts": { "test": "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage" } } ``` ## Component Testing ### Testing Vue Components in Nuxt 2 Here's how to test components in Nuxt 2: ```js // components/Counter.vue <template> <div> <p class="count">Count: {{ count }}</p> <button @click="increment">Increment</button> </div> </template> <script> export default { data() { return { count: 0 }; }, methods: { increment() { this.count++; } } }; </script> // tests/components/Counter.spec.js import { mount } from '@vue/test-utils'; import Counter from '@/components/Counter.vue'; describe('Counter Component', () => { test('renders and increments count when button is clicked', async () => { const wrapper = mount(Counter); // Initial state expect(wrapper.find('.count').text()).toBe('Count: 0'); // Interact with component await wrapper.find('button').trigger('click'); // Check updated state expect(wrapper.find('.count').text()).toBe('Count: 1'); }); }); ``` ### Testing with Vuex Store ```js // components/UserInfo.vue <template> <div> <p v-if="user">{{ user.name }}</p> <button @click="fetchUser">Load User</button> </div> </template> <script> import { mapState, mapActions } from 'vuex'; export default { computed: { ...mapState({ user: state => state.user.currentUser }) }, methods: { ...mapActions('user', ['fetchUser']) } }; </script> // tests/components/UserInfo.spec.js import { createLocalVue, mount } from '@vue/test-utils'; import Vuex from 'vuex'; import UserInfo from '@/components/UserInfo.vue'; const localVue = createLocalVue(); localVue.use(Vuex); describe('UserInfo Component', () => { let actions; let store; let state; beforeEach(() => { actions = { 'user/fetchUser': jest.fn() }; state = { user: { currentUser: null } }; store = new Vuex.Store({ state, actions }); }); test('calls store action when button is clicked', async () => { const wrapper = mount(UserInfo, { store, localVue }); await wrapper.find('button').trigger('click'); expect(actions['user/fetchUser']).toHaveBeenCalled(); }); test('displays user name when user is set', async () => { // Set user in the store state.user.currentUser = { name: 'John Doe' }; const wrapper = mount(UserInfo, { store, localVue }); expect(wrapper.text()).toContain('John Doe'); }); }); ``` ## Testing Pages ### Testing Nuxt 2 Pages ```js // pages/index.vue <template> <div> <h1>{{ title }}</h1> <p>{{ description }}</p> </div> </template> <script> export default { data() { return { title: 'Welcome to Nuxt', description: 'Nuxt.js is awesome!' }; } }; </script> // tests/pages/index.spec.js import { mount } from '@vue/test-utils'; import IndexPage from '@/pages/index.vue'; describe('Index Page', () => { test('renders the correct title and description', () => { const wrapper = mount(IndexPage); expect(wrapper.find('h1').text()).toBe('Welcome to Nuxt'); expect(wrapper.find('p').text()).toBe('Nuxt.js is awesome!'); }); }); ``` ### Testing with asyncData and fetch ```js // pages/users/_id.vue <template> <div> <h1>User Profile</h1> <div v-if="user"> <p>Name: {{ user.name }}</p> <p>Email: {{ user.email }}</p> </div> </div> </template> <script> export default { async asyncData({ params, $axios }) { const user = await $axios.$get(`/api/users/${params.id}`); return { user }; } }; </script> // tests/pages/users/_id.spec.js import { mount } from '@vue/test-utils'; import UserPage from '@/pages/users/_id.vue'; describe('User Page', () => { test('renders user data from asyncData', async () => { // Mock asyncData result const mockUser = { name: 'John Doe', email: 'john@example.com' }; UserPage.asyncData = jest.fn().mockResolvedValue({ user: mockUser }); // Mount component with asyncData pre-resolved const asyncDataResult = await UserPage.asyncData({ params: { id: '1' }, $axios: { $get: jest.fn().mockResolvedValue(mockUser) } }); const wrapper = mount(UserPage, { data() { return asyncDataResult; } }); expect(wrapper.find('h1').text()).toBe('User Profile'); expect(wrapper.text()).toContain('John Doe'); expect(wrapper.text()).toContain('john@example.com'); }); }); ``` ## Testing Vuex Store ### Testing Vuex Actions and Mutations ```js // store/counter.js export const state = () => ({ count: 0, }); export const mutations = { increment(state) { state.count++; }, setValue(state, value) { state.count = value; }, }; export const actions = { async fetchCount({ commit }) { const count = await this.$axios.$get('/api/count'); commit('setValue', count); return count; }, }; export const getters = { doubleCount: (state) => state.count * 2, }; // tests/store/counter.spec.js import { mutations, actions, getters } from '@/store/counter'; describe('Counter Store', () => { // Test mutations describe('mutations', () => { test('increment increments count by 1', () => { const state = { count: 0 }; mutations.increment(state); expect(state.count).toBe(1); }); test('setValue sets count to given value', () => { const state = { count: 0 }; mutations.setValue(state, 10); expect(state.count).toBe(10); }); }); // Test getters describe('getters', () => { test('doubleCount returns count multiplied by 2', () => { const state = { count: 5 }; expect(getters.doubleCount(state)).toBe(10); }); }); // Test actions describe('actions', () => { test('fetchCount commits the fetched count', async () => { const count = 42; const commit = jest.fn(); // Mock Nuxt's $axios const $axios = { $get: jest.fn().mockResolvedValue(count), }; // Mock context const context = { commit, $axios, }; // Create mock for Nuxt context const nuxtContext = { $axios, }; // Bind nuxtContext to the action (simulating Nuxt's injection) const boundActions = {}; Object.keys(actions).forEach((key) => { boundActions[key] = actions[key].bind(nuxtContext); }); const result = await boundActions.fetchCount({ commit }); expect(result).toBe(count); expect(commit).toHaveBeenCalledWith('setValue', count); }); }); }); ``` ## Testing Nuxt Plugins ```js // plugins/format.js export default ({ app }, inject) => { inject('formatDate', (date) => { if (!date) return ''; return new Date(date).toLocaleDateString(); }); }; // tests/plugins/format.spec.js import format from '@/plugins/format'; describe('Format Plugin', () => { test('formatDate formats date correctly', () => { // Create mock context and inject function const inject = jest.fn(); const context = { app: {} }; // Call plugin format(context, inject); // Check that formatDate was injected expect(inject).toHaveBeenCalledWith('formatDate', expect.any(Function)); // Get the injected function const formatDate = inject.mock.calls[0][1]; // Test the function const date = new Date('2023-01-15'); expect(formatDate(date)).toBe(date.toLocaleDateString()); expect(formatDate(null)).toBe(''); }); }); ``` ## Testing API Requests ### Mocking Axios in Nuxt 2 ```js // components/UserList.vue <template> <div> <h2>Users</h2> <ul v-if="users.length"> <li v-for="user in users" :key="user.id">{{ user.name }}</li> </ul> <button @click="loadUsers">Load Users</button> </div> </template> <script> export default { data() { return { users: [] }; }, methods: { async loadUsers() { this.users = await this.$axios.$get('/api/users'); } } }; </script> // tests/components/UserList.spec.js import { mount } from '@vue/test-utils'; import UserList from '@/components/UserList.vue'; describe('UserList', () => { test('loads and displays users when button is clicked', async () => { // Mock data const users = [ { id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Smith' } ]; // Create mock $axios const $axios = { $get: jest.fn().mockResolvedValue(users) }; // Mount with mocks const wrapper = mount(UserList, { mocks: { $axios } }); // Initially no users expect(wrapper.findAll('li').length).toBe(0); // Click button to load users await wrapper.find('button').trigger('click'); // Check that $axios was called correctly expect($axios.$get).toHaveBeenCalledWith('/api/users'); // Wait for DOM to update await wrapper.vm.$nextTick(); // Check that users are displayed const listItems = wrapper.findAll('li'); expect(listItems.length).toBe(2); expect(listItems.at(0).text()).toBe('John Doe'); expect(listItems.at(1).text()).toBe('Jane Smith'); }); }); ``` ## End-to-End Testing with Cypress ```js // cypress/integration/navigation.spec.js describe('Navigation', () => { it('navigates from home page to about page', () => { // Visit the home page cy.visit('/'); // Find the link to the about page and click it cy.get('nav').contains('About').click(); // Verify we're on the about page cy.url().should('include', '/about'); cy.get('h1').should('contain', 'About'); }); it('completes a login flow', () => { // Visit login page cy.visit('/login'); // Fill the form cy.get('input[name="email"]').type('test@example.com'); cy.get('input[name="password"]').type('password123'); // Submit the form cy.get('form').submit(); // Verify redirect to dashboard cy.url().should('include', '/dashboard'); // Verify user is logged in cy.get('.user-profile').should('contain', 'test@example.com'); }); }); ``` ## Testing Middleware ```js // middleware/auth.js export default function ({ store, redirect, route }) { if (!store.state.auth.loggedIn && route.path !== '/login') { return redirect('/login'); } } // tests/middleware/auth.spec.js import authMiddleware from '@/middleware/auth'; describe('Auth Middleware', () => { test('redirects to login when user is not authenticated', () => { // Mock context const redirect = jest.fn(); const context = { store: { state: { auth: { loggedIn: false, }, }, }, route: { path: '/dashboard', }, redirect, }; // Call middleware authMiddleware(context); // Check that redirect was called expect(redirect).toHaveBeenCalledWith('/login'); }); test('allows access when user is authenticated', () => { // Mock context with authenticated user const redirect = jest.fn(); const context = { store: { state: { auth: { loggedIn: true, }, }, }, route: { path: '/dashboard', }, redirect, }; // Call middleware authMiddleware(context); // Check that redirect was not called expect(redirect).not.toHaveBeenCalled(); }); test('allows access to login page even when not authenticated', () => { // Mock context with unauthenticated user but on login page const redirect = jest.fn(); const context = { store: { state: { auth: { loggedIn: false, }, }, }, route: { path: '/login', }, redirect, }; // Call middleware authMiddleware(context); // Check that redirect was not called expect(redirect).not.toHaveBeenCalled(); }); }); ``` ## Testing with Nuxt-Specific Helpers For more complex Nuxt 2 testing, you can use `@nuxt/test-utils`: ```js // tests/pages/about.spec.js import { setupTest, createPage } from '@nuxt/test-utils'; describe('About Page', () => { setupTest({ testDir: __dirname, fixture: '../', browser: true, }); test('renders the about page with correct content', async () => { const page = await createPage('/about'); const html = await page.innerHTML('h1'); expect(html).toContain('About Us'); }); }); ``` ## Best Practices for Nuxt 2 Testing When testing Nuxt 2 applications, remember to: 1. Mock Nuxt-specific objects like `$axios`, `$store`, and `$router` 2. Handle async operations correctly with `await` and `$nextTick()` 3. Pre-resolve `asyncData` and `fetch` when testing pages 4. Use a combination of unit, integration, and end-to-end tests 5. Keep test files organized, mirroring your application structure 6. Use snapshot testing sparingly, focusing on behavior over implementation details