Spaces:
Paused
Paused
File size: 6,138 Bytes
9ada4bc |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
import { describe, vi, it, expect, afterEach } from 'vitest'
import {
Interceptor,
getGlobalSymbol,
deleteGlobalSymbol,
InterceptorReadyState,
} from './Interceptor'
import { nextTickAsync } from './utils/nextTick'
const symbol = Symbol('test')
afterEach(() => {
deleteGlobalSymbol(symbol)
})
it('does not set a maximum listeners limit', () => {
const interceptor = new Interceptor(symbol)
expect(interceptor['emitter'].getMaxListeners()).toBe(0)
})
describe('on()', () => {
it('adds a new listener using "on()"', () => {
const interceptor = new Interceptor(symbol)
expect(interceptor['emitter'].listenerCount('event')).toBe(0)
const listener = vi.fn()
interceptor.on('event', listener)
expect(interceptor['emitter'].listenerCount('event')).toBe(1)
})
})
describe('once()', () => {
it('calls the listener only once', () => {
const interceptor = new Interceptor(symbol)
const listener = vi.fn()
interceptor.once('foo', listener)
expect(listener).not.toHaveBeenCalled()
interceptor['emitter'].emit('foo', 'bar')
expect(listener).toHaveBeenCalledTimes(1)
expect(listener).toHaveBeenCalledWith('bar')
listener.mockReset()
interceptor['emitter'].emit('foo', 'baz')
interceptor['emitter'].emit('foo', 'xyz')
expect(listener).toHaveBeenCalledTimes(0)
})
})
describe('off()', () => {
it('removes a listener using "off()"', () => {
const interceptor = new Interceptor(symbol)
expect(interceptor['emitter'].listenerCount('event')).toBe(0)
const listener = vi.fn()
interceptor.on('event', listener)
expect(interceptor['emitter'].listenerCount('event')).toBe(1)
interceptor.off('event', listener)
expect(interceptor['emitter'].listenerCount('event')).toBe(0)
})
})
describe('persistence', () => {
it('stores global reference to the applied interceptor', () => {
const interceptor = new Interceptor(symbol)
interceptor.apply()
expect(getGlobalSymbol(symbol)).toEqual(interceptor)
})
it('deletes global reference when the interceptor is disposed', () => {
const interceptor = new Interceptor(symbol)
interceptor.apply()
interceptor.dispose()
expect(getGlobalSymbol(symbol)).toBeUndefined()
})
})
describe('readyState', () => {
it('sets the state to "INACTIVE" when the interceptor is created', () => {
const interceptor = new Interceptor(symbol)
expect(interceptor.readyState).toBe(InterceptorReadyState.INACTIVE)
})
it('leaves state as "INACTIVE" if the interceptor failed the environment check', async () => {
class MyInterceptor extends Interceptor<any> {
protected checkEnvironment(): boolean {
return false
}
}
const interceptor = new MyInterceptor(symbol)
interceptor.apply()
expect(interceptor.readyState).toBe(InterceptorReadyState.INACTIVE)
})
it('perfroms state transition when the interceptor is applying', async () => {
const interceptor = new Interceptor(symbol)
interceptor.apply()
// The interceptor's state transitions to APPLIED immediately.
// The only exception is if something throws during the setup.
expect(interceptor.readyState).toBe(InterceptorReadyState.APPLIED)
})
it('perfroms state transition when disposing of the interceptor', async () => {
const interceptor = new Interceptor(symbol)
interceptor.apply()
interceptor.dispose()
// The interceptor's state transitions to DISPOSED immediately.
// The only exception is if something throws during the teardown.
expect(interceptor.readyState).toBe(InterceptorReadyState.DISPOSED)
})
})
describe('apply', () => {
it('does not apply the same interceptor multiple times', () => {
const interceptor = new Interceptor(symbol)
const setupSpy = vi.spyOn(
interceptor,
// @ts-expect-error Protected property spy.
'setup'
)
// Intentionally apply the same interceptor multiple times.
interceptor.apply()
interceptor.apply()
interceptor.apply()
// The "setup" must not be called repeatedly.
expect(setupSpy).toHaveBeenCalledTimes(1)
expect(getGlobalSymbol(symbol)).toEqual(interceptor)
})
it('does not call "apply" if the interceptor fails environment check', () => {
class MyInterceptor extends Interceptor<{}> {
checkEnvironment() {
return false
}
}
const interceptor = new MyInterceptor(Symbol('test'))
const setupSpy = vi.spyOn(
interceptor,
// @ts-expect-error Protected property spy.
'setup'
)
interceptor.apply()
expect(setupSpy).not.toHaveBeenCalled()
})
it('proxies listeners from new interceptor to already running interceptor', () => {
const firstInterceptor = new Interceptor(symbol)
const secondInterceptor = new Interceptor(symbol)
firstInterceptor.apply()
const firstListener = vi.fn()
firstInterceptor.on('test', firstListener)
secondInterceptor.apply()
const secondListener = vi.fn()
secondInterceptor.on('test', secondListener)
// Emitting event in the first interceptor will bubble to the second one.
firstInterceptor['emitter'].emit('test', 'hello world')
expect(firstListener).toHaveBeenCalledTimes(1)
expect(firstListener).toHaveBeenCalledWith('hello world')
expect(secondListener).toHaveBeenCalledTimes(1)
expect(secondListener).toHaveBeenCalledWith('hello world')
expect(secondInterceptor['emitter'].listenerCount('test')).toBe(0)
})
})
describe('dispose', () => {
it('removes all listeners when the interceptor is disposed', async () => {
const interceptor = new Interceptor(symbol)
interceptor.apply()
const listener = vi.fn()
interceptor.on('test', listener)
interceptor.dispose()
// Even after emitting an event, the listener must not get called.
interceptor['emitter'].emit('test')
expect(listener).not.toHaveBeenCalled()
// The listener must not be called on the next tick either.
await nextTickAsync(() => {
interceptor['emitter'].emit('test')
expect(listener).not.toHaveBeenCalled()
})
})
})
|