mirror of
https://github.com/github/codeql-action.git
synced 2025-12-29 02:30:11 +08:00
206 lines
6.0 KiB
TypeScript
206 lines
6.0 KiB
TypeScript
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()
|
|
})
|
|
})
|
|
})
|