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
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
|
|
}
|
|
}
|