You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

248 lines
5.9 KiB

/**
* Test Utilities for TimeSafari Component Testing
*
* Provides standardized test patterns, helpers, and utilities
* for consistent component testing across the application.
*
* @author Matthew Raymer
*/
import { mount, VueWrapper } from '@vue/test-utils'
import { ComponentPublicInstance } from 'vue'
import { vi } from 'vitest'
/**
* Standardized test setup interface
*/
export interface TestSetup {
wrapper: VueWrapper<ComponentPublicInstance> | null
mountComponent: (props?: any) => VueWrapper<ComponentPublicInstance>
cleanup: () => void
}
/**
* Standardized beforeEach pattern for all component tests
* @param component - Vue component to test
* @param defaultProps - Default props for the component
* @param globalOptions - Global options for mounting
* @returns Test setup object
*/
export const createTestSetup = (
component: any,
defaultProps = {},
globalOptions = {}
) => {
let wrapper: VueWrapper<ComponentPublicInstance> | null = null
const mountComponent = (props = {}) => {
return mount(component, {
props: { ...defaultProps, ...props },
global: globalOptions
})
}
const cleanup = () => {
if (wrapper) {
wrapper.unmount()
wrapper = null
}
}
return {
wrapper,
mountComponent,
cleanup
}
}
/**
* Standardized beforeEach function
* @param setup - Test setup object
*/
export const standardBeforeEach = (setup: TestSetup) => {
setup.wrapper = null
}
/**
* Standardized afterEach function
* @param setup - Test setup object
*/
export const standardAfterEach = (setup: TestSetup) => {
if (setup.wrapper) {
setup.wrapper.unmount()
setup.wrapper = null
}
}
/**
* Wait for async operations to complete
* @param ms - Milliseconds to wait
* @returns Promise that resolves after the specified time
*/
export const waitForAsync = (ms: number = 0): Promise<void> => {
return new Promise(resolve => setTimeout(resolve, ms))
}
/**
* Wait for Vue to finish updating
* @param wrapper - Vue test wrapper
* @returns Promise that resolves after Vue updates
*/
export const waitForVueUpdate = async (wrapper: VueWrapper<ComponentPublicInstance>) => {
await wrapper.vm.$nextTick()
await waitForAsync(10) // Small delay to ensure all updates are complete
}
/**
* Create mock store for testing
* @returns Mock Vuex store
*/
export const createMockStore = () => ({
state: {
user: { isRegistered: false },
contacts: [],
projects: []
},
getters: {
isUserRegistered: (state: any) => state.user.isRegistered,
getContacts: (state: any) => state.contacts,
getProjects: (state: any) => state.projects
},
mutations: {
setUserRegistered: vi.fn(),
setContacts: vi.fn(),
setProjects: vi.fn()
},
actions: {
fetchContacts: vi.fn(),
fetchProjects: vi.fn(),
updateUser: vi.fn()
}
})
/**
* Create mock router for testing
* @returns Mock Vue router
*/
export const createMockRouter = () => ({
push: vi.fn(),
replace: vi.fn(),
go: vi.fn(),
back: vi.fn(),
forward: vi.fn(),
currentRoute: {
value: {
name: 'home',
path: '/',
params: {},
query: {}
}
}
})
/**
* Create mock service for testing
* @returns Mock service object
*/
export const createMockService = () => ({
getData: vi.fn().mockResolvedValue([]),
saveData: vi.fn().mockResolvedValue(true),
deleteData: vi.fn().mockResolvedValue(true),
updateData: vi.fn().mockResolvedValue(true)
})
/**
* Performance testing utilities
*/
export const performanceUtils = {
/**
* Measure execution time of a function
* @param fn - Function to measure
* @returns Object with timing information
*/
measureTime: async (fn: () => any) => {
const start = performance.now()
const result = await fn()
const end = performance.now()
return {
result,
duration: end - start,
start,
end
}
},
/**
* Check if performance is within acceptable limits
* @param duration - Duration in milliseconds
* @param threshold - Maximum acceptable duration
* @returns Boolean indicating if performance is acceptable
*/
isWithinThreshold: (duration: number, threshold: number = 200) => {
return duration < threshold
}
}
/**
* Accessibility testing utilities
*/
export const accessibilityUtils = {
/**
* Check if element has required ARIA attributes
* @param element - DOM element to check
* @param requiredAttributes - Array of required ARIA attributes
* @returns Boolean indicating if all required attributes are present
*/
hasRequiredAriaAttributes: (element: any, requiredAttributes: string[]) => {
return requiredAttributes.every(attr =>
element.attributes(attr) !== undefined
)
},
/**
* Check if element is keyboard accessible
* @param element - DOM element to check
* @returns Boolean indicating if element is keyboard accessible
*/
isKeyboardAccessible: (element: any) => {
const tabindex = element.attributes('tabindex')
const role = element.attributes('role')
return tabindex !== undefined || role === 'button' || role === 'link'
}
}
/**
* Error testing utilities
*/
export const errorUtils = {
/**
* Test component with various invalid prop combinations
* @param mountComponent - Function to mount component
* @param invalidProps - Array of invalid prop combinations
* @returns Array of test results
*/
testInvalidProps: async (mountComponent: Function, invalidProps: any[]) => {
const results = []
for (const props of invalidProps) {
try {
const wrapper = mountComponent(props)
results.push({
props,
success: true,
error: null,
wrapper: wrapper.exists()
})
} catch (error) {
results.push({
props,
success: false,
error: error instanceof Error ? error.message : String(error),
wrapper: false
})
}
}
return results
}
}