Files
api/.cursor/rules/web-testing-rules.mdc
Pujit Mehrotra 557b03f882 feat: createDockerFolder & setDockerFolderChildren mutations (#1558)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added the ability to create Docker folders and set folder children via
new GraphQL mutations in the Docker organizer.
* Enhanced organizer management with pure functions for folder creation
and child assignment, ensuring immutability.

* **Bug Fixes**
* Improved validation to prevent empty or invalid folder entries in the
organizer structure.

* **Tests**
* Added comprehensive tests for folder creation, child assignment, and
organizer resolution, including edge cases and immutability checks.
* Updated test guidelines to focus on observable behavior and error
handling best practices.

* **Documentation**
  * Expanded testing best practices in project documentation.

* **Chores**
  * Updated ignore rules for local configuration files.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-04 12:22:51 -04:00

245 lines
7.6 KiB
Plaintext

---
description:
globs: **/*.test.ts,**/__test__/components/**/*.ts,**/__test__/store/**/*.ts,**/__test__/mocks/**/*.ts
alwaysApply: false
---
## General Testing Best Practices
- **Error Testing:** Use `.rejects.toThrow()` without arguments to test that functions throw errors. Don't test exact error message strings unless the message format is specifically what you're testing
- **Focus on Behavior:** Test what the code does, not implementation details like exact error message wording
## Vue Component Testing Best Practices
- This is a Nuxt.js app but we are testing with vitest outside of the Nuxt environment
- Nuxt is currently set to auto import so some vue files may need compute or ref imported
- Use pnpm when running termical commands and stay within the web directory.
- The directory for tests is located under `web/__test__` when running test just run `pnpm test`
### Setup
- Use `mount` from Vue Test Utils for component testing
- Stub complex child components that aren't the focus of the test
- Mock external dependencies and services
```typescript
import { mount } from '@vue/test-utils';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { createTestingPinia } from '@pinia/testing'
import { useSomeStore } from '@/stores/myStore'
import YourComponent from '~/components/YourComponent.vue';
// Mock dependencies
vi.mock('~/helpers/someHelper', () => ({
SOME_CONSTANT: 'mocked-value',
}));
describe('YourComponent', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('renders correctly', () => {
const wrapper = mount(YourComponent, {
global: {
plugins: [createTestingPinia()],
stubs: {
// Stub child components when needed
ChildComponent: true,
},
},
});
const store = useSomeStore() // uses the testing pinia!
// state can be directly manipulated
store.name = 'my new name'
// actions are stubbed by default, meaning they don't execute their code by default.
// See below to customize this behavior.
store.someAction()
expect(store.someAction).toHaveBeenCalledTimes(1)
// Assertions on components
expect(wrapper.text()).toContain('Expected content');
});
});
```
### Testing Patterns
- Test component behavior and output, not implementation details
- Verify that the expected elements are rendered
- Test component interactions (clicks, inputs, etc.)
- Check for expected prop handling and event emissions
- Use `createTestingPinia()` for mocking stores in components
### Finding Elements
- Use semantic queries like `find('button')` or `find('[data-test="id"]')` but prefer not to use data test ID's
- Find components with `findComponent(ComponentName)`
- Use `findAll` to check for multiple elements
### Assertions
- Assert on rendered text content with `wrapper.text()`
- Assert on element attributes with `element.attributes()`
- Verify element existence with `expect(element.exists()).toBe(true)`
- Check component state through rendered output
### Component Interaction
- Trigger events with `await element.trigger('click')`
- Set input values with `await input.setValue('value')`
- Test emitted events with `wrapper.emitted()`
### Mocking
- Mock external services and API calls
- Prefer not using mocks whenever possible
- Use `vi.mock()` for module-level mocks
- Specify return values for component methods with `vi.spyOn()`
- Reset mocks between tests with `vi.clearAllMocks()`
- Frequently used mocks are stored under `web/test/mocks`
### Async Testing
- Use `await nextTick()` for DOM updates
- Use `flushPromises()` for more complex promise chains
- Always await async operations before making assertions
## Store Testing with Pinia
### Basic Setup
- When testing Store files use `createPinia` and `setActivePinia`
```typescript
import { createPinia, setActivePinia } from 'pinia';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { useYourStore } from '~/store/your-store';
// Mock declarations must be at top level due to hoisting
const mockDependencyFn = vi.fn();
// Module mocks must use factory functions
vi.mock('~/store/dependency', () => ({
useDependencyStore: () => ({
someMethod: mockDependencyFn,
someProperty: 'mockValue'
})
}));
describe('Your Store', () => {
let store: ReturnType<typeof useYourStore>;
beforeEach(() => {
setActivePinia(createPinia());
store = useYourStore();
vi.clearAllMocks();
});
afterEach(() => {
vi.resetAllMocks();
});
it('tests some action', () => {
store.someAction();
expect(mockDependencyFn).toHaveBeenCalled();
});
});
```
### Important Guidelines
1. **Store Initialization**
- Use `createPinia()` instead of `createTestingPinia()` for most cases
- Only use `createTestingPinia` if you specifically need its testing features
- Let stores initialize with their natural default state instead of forcing initial state
- Do not mock the store we're actually testing in the test file. That's why we're using `createPinia()`
2. **Vue Reactivity**
- Ensure Vue reactivity imports are added to original store files as they may be missing because Nuxt auto import was turned on
- Don't rely on Nuxt auto-imports in tests
```typescript
// Required in store files, even with Nuxt auto-imports
import { computed, ref, watchEffect } from 'vue';
```
3. **Mocking Best Practices**
- Place all mock declarations at the top level
- Use factory functions for module mocks to avoid hoisting issues
```typescript
// ❌ Wrong - will cause hoisting issues
const mockFn = vi.fn();
vi.mock('module', () => ({ method: mockFn }));
// ✅ Correct - using factory function
vi.mock('module', () => {
const mockFn = vi.fn();
return { method: mockFn };
});
```
4. **Testing Actions**
- Test action side effects and state changes
- Verify actions are called with correct parameters
- Mock external dependencies appropriately
```typescript
it('should handle action correctly', () => {
store.yourAction();
expect(mockDependencyFn).toHaveBeenCalledWith(
expectedArg1,
expectedArg2
);
expect(store.someState).toBe(expectedValue);
});
```
5. **Common Pitfalls**
- Don't mix mock declarations and module mocks incorrectly
- Avoid relying on Nuxt's auto-imports in test environment
- Clear mocks between tests to ensure isolation
- Remember that `vi.mock()` calls are hoisted
### Testing State & Getters
- Test computed properties by accessing them directly
- Verify state changes after actions
- Test getter dependencies are properly mocked
```typescript
it('computes derived state correctly', () => {
store.setState('new value');
expect(store.computedValue).toBe('expected result');
});
```
### Testing Complex Interactions
- Test store interactions with other stores
- Verify proper error handling
- Test async operations completely
```typescript
it('handles async operations', async () => {
const promise = store.asyncAction();
expect(store.status).toBe('loading');
await promise;
expect(store.status).toBe('success');
});
```
### Testing Actions
- Verify actions are called with the right parameters
- Test action side effects if not stubbed
- Override specific action implementations when needed
```typescript
// Test action calls
store.yourAction(params);
expect(store.yourAction).toHaveBeenCalledWith(params);
// Test with real implementation
const pinia = createTestingPinia({
createSpy: vi.fn,
stubActions: false,
});
```
### Testing State & Getters
- Set initial state for focused testing
- Test computed properties by accessing them directly
- Verify state changes by updating the store