@idooel/runtime-context
Version:
Runtime data pool with namespaces, stackable contexts, subscriptions and optional persistence. Vue adapter included.
208 lines (158 loc) • 5.88 kB
text/typescript
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { Vue2DataPoolPlugin } from '../../src/vue2/plugin'
describe('Vue2DataPoolPlugin', () => {
let mockVue: any
beforeEach(() => {
mockVue = {
prototype: {},
$idooelDataPool: null,
mixin: vi.fn(),
observable: vi.fn((obj) => obj)
}
vi.clearAllMocks()
})
it('should install plugin with default options', () => {
Vue2DataPoolPlugin.install(mockVue)
expect(mockVue.prototype.$idooelDataPool).toBeDefined()
expect(mockVue.$idooelDataPool).toBeDefined()
expect(mockVue.mixin).toHaveBeenCalled()
})
it('should install plugin with custom pool', () => {
const customPool = { custom: 'pool' } as any
Vue2DataPoolPlugin.install(mockVue, { pool: customPool })
expect(mockVue.prototype.$idooelDataPool).toBe(customPool)
expect(mockVue.$idooelDataPool).toBe(customPool)
})
it('should skip global inject when disabled', () => {
Vue2DataPoolPlugin.install(mockVue, { globalInject: false })
expect(mockVue.prototype.$idooelDataPool).toBeUndefined()
expect(mockVue.$idooelDataPool).toBeNull()
})
it('should skip mixin when disabled', () => {
Vue2DataPoolPlugin.install(mockVue, { mixin: false })
expect(mockVue.mixin).not.toHaveBeenCalled()
})
it('should add mixin for subscription management', () => {
Vue2DataPoolPlugin.install(mockVue)
expect(mockVue.mixin).toHaveBeenCalledWith(
expect.objectContaining({
created: expect.any(Function),
beforeDestroy: expect.any(Function)
})
)
})
})
describe('Vue Integration - Component Lifecycle', () => {
let mockVue: any
let mockComponent: any
let mixinOptions: any
beforeEach(() => {
mockVue = {
prototype: {},
$idooelDataPool: null,
mixin: vi.fn()
}
mockComponent = {
_idooelDataPoolSubscriptions: []
}
Vue2DataPoolPlugin.install(mockVue)
// Get the mixin options
mixinOptions = mockVue.mixin.mock.calls[0][0]
})
it('should initialize subscription array on component creation', () => {
mixinOptions.created.call(mockComponent)
expect(mockComponent._idooelDataPoolSubscriptions).toEqual([])
})
it('should cleanup subscriptions on component destroy', () => {
const unsubscribe1 = vi.fn()
const unsubscribe2 = vi.fn()
mockComponent._idooelDataPoolSubscriptions = [unsubscribe1, unsubscribe2]
mixinOptions.beforeDestroy.call(mockComponent)
expect(unsubscribe1).toHaveBeenCalled()
expect(unsubscribe2).toHaveBeenCalled()
expect(mockComponent._idooelDataPoolSubscriptions).toEqual([])
})
it('should handle cleanup when no subscriptions exist', () => {
delete mockComponent._idooelDataPoolSubscriptions
expect(() => mixinOptions.beforeDestroy.call(mockComponent)).not.toThrow()
})
it('should handle errors in subscription lifecycle gracefully', () => {
const badUnsubscribe = vi.fn(() => {
throw new Error('Unsubscribe error')
})
mockComponent._idooelDataPoolSubscriptions = [badUnsubscribe]
// Should not throw even if unsubscribe fails
expect(() => mixinOptions.beforeDestroy.call(mockComponent)).not.toThrow()
// Bad unsubscribe should still be called
expect(badUnsubscribe).toHaveBeenCalled()
// Subscriptions should be cleared even if errors occurred
expect(mockComponent._idooelDataPoolSubscriptions).toEqual([])
})
})
describe('Vue Integration - Data Pool Integration', () => {
let mockVue: any
let mockPool: any
beforeEach(() => {
mockPool = {
get: vi.fn(),
set: vi.fn(),
delete: vi.fn(),
subscribe: vi.fn(() => vi.fn()),
createDataPool: vi.fn(() => mockPool)
}
mockVue = {
prototype: {},
$idooelDataPool: null,
mixin: vi.fn(),
observable: vi.fn((obj) => obj)
}
Vue2DataPoolPlugin.install(mockVue, { pool: mockPool })
})
it('should provide data pool through prototype', () => {
const mockComponent = {}
// Simulate Vue instance access
Object.setPrototypeOf(mockComponent, mockVue.prototype)
expect(mockComponent.$idooelDataPool).toBe(mockPool)
})
it('should allow direct pool operations through prototype', () => {
mockPool.get.mockReturnValue('test-value')
const mockComponent = {}
Object.setPrototypeOf(mockComponent, mockVue.prototype)
mockComponent.$idooelDataPool.set('test', 'key', 'value')
const value = mockComponent.$idooelDataPool.get('test', 'key')
expect(mockPool.set).toHaveBeenCalledWith('test', 'key', 'value')
expect(mockPool.get).toHaveBeenCalledWith('test', 'key')
expect(value).toBe('test-value')
})
})
describe('Vue Integration - Observable Integration', () => {
let mockVue: any
let mockPool: any
beforeEach(() => {
mockPool = {
get: vi.fn(),
subscribe: vi.fn(() => vi.fn())
}
mockVue = {
prototype: {},
$idooelDataPool: null,
mixin: vi.fn(),
observable: vi.fn((obj) => obj)
}
Vue2DataPoolPlugin.install(mockVue, { pool: mockPool })
})
it('should provide Vue.observable for reactive values', () => {
mockPool.get.mockReturnValue('initial')
const mockComponent = {}
Object.setPrototypeOf(mockComponent, mockVue.prototype)
// Test that Vue.observable is called (simulating usePoolValue behavior)
mockVue.observable({ data: 'initial' })
expect(mockVue.observable).toHaveBeenCalledWith({ data: 'initial' })
})
it('should handle Vue.observable errors gracefully', () => {
mockVue.observable.mockImplementation(() => {
throw new Error('Observable error')
})
expect(() => mockVue.observable({ data: 'test' })).toThrow('Observable error')
})
})