Files
bruno/packages/bruno-js/tests/hooks-executor.spec.js
sanish-bruno 2c474e8052 feat: introduce HooksExecutor and HooksConsolidator for optimized hook execution
- Added HooksExecutor to centralize hook execution logic across CLI and Electron.
- Implemented HooksConsolidator to improve performance by consolidating multiple hook levels into a single execution context.
- Refactored existing hook management to utilize the new executor and consolidator, enhancing efficiency and reducing resource usage.
- Updated runSingleRequest and network IPC to leverage consolidated hook execution for better performance.
- Introduced comprehensive tests for the new hooks functionality, ensuring reliability and correctness.
2026-01-28 19:13:57 +05:30

165 lines
5.4 KiB
JavaScript

/**
* Unit tests for Hooks Executor
*/
const {
executeHooksForLevel,
executeConsolidatedHooks,
executeAllHookLevels,
createHookExecutor,
HOOK_EVENTS
} = require('../src/runtime/hooks-executor');
// Mock the HooksRuntime
jest.mock('../src/runtime/hooks-runtime', () => {
return jest.fn().mockImplementation(() => ({
runHooks: jest.fn().mockResolvedValue({
hookManager: {
call: jest.fn().mockResolvedValue(undefined),
dispose: jest.fn()
},
envVariables: {},
runtimeVariables: {},
persistentEnvVariables: {},
globalEnvironmentVariables: {}
})
}));
});
describe('Hooks Executor', () => {
const mockOptions = {
request: { url: 'http://test.com' },
envVariables: {},
runtimeVariables: {},
collectionPath: '/test/collection',
onConsoleLog: jest.fn(),
processEnvVars: {},
scriptingConfig: { runtime: 'quickjs' },
runRequestByItemPathname: jest.fn(),
collectionName: 'Test Collection'
};
beforeEach(() => {
jest.clearAllMocks();
});
describe('HOOK_EVENTS', () => {
it('should have correct event names', () => {
expect(HOOK_EVENTS.HTTP_BEFORE_REQUEST).toBe('http:beforeRequest');
expect(HOOK_EVENTS.HTTP_AFTER_RESPONSE).toBe('http:afterResponse');
expect(HOOK_EVENTS.RUNNER_BEFORE_COLLECTION_RUN).toBe('runner:beforeCollectionRun');
expect(HOOK_EVENTS.RUNNER_AFTER_COLLECTION_RUN).toBe('runner:afterCollectionRun');
});
it('should be frozen', () => {
expect(Object.isFrozen(HOOK_EVENTS)).toBe(true);
});
});
describe('executeHooksForLevel()', () => {
it('should return null for empty hooks file', async () => {
const result = await executeHooksForLevel('', HOOK_EVENTS.HTTP_BEFORE_REQUEST, {}, mockOptions);
expect(result).toBeNull();
});
it('should return null for whitespace-only hooks file', async () => {
const result = await executeHooksForLevel(' ', HOOK_EVENTS.HTTP_BEFORE_REQUEST, {}, mockOptions);
expect(result).toBeNull();
});
it('should execute hooks for valid hooks file', async () => {
const HooksRuntime = require('../src/runtime/hooks-runtime');
await executeHooksForLevel('console.log("test")', HOOK_EVENTS.HTTP_BEFORE_REQUEST, {}, mockOptions);
expect(HooksRuntime).toHaveBeenCalled();
});
it('should handle errors gracefully', async () => {
const HooksRuntime = require('../src/runtime/hooks-runtime');
HooksRuntime.mockImplementationOnce(() => ({
runHooks: jest.fn().mockRejectedValue(new Error('Test error'))
}));
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
const result = await executeHooksForLevel('test()', HOOK_EVENTS.HTTP_BEFORE_REQUEST, {}, mockOptions);
expect(result).toBeNull();
consoleSpy.mockRestore();
});
});
describe('executeConsolidatedHooks()', () => {
it('should execute consolidated hooks', async () => {
const HooksRuntime = require('../src/runtime/hooks-runtime');
const extractedHooks = {
collectionHooks: 'collection()',
folderHooks: [{ hooks: 'folder()' }],
requestHooks: 'request()'
};
await executeConsolidatedHooks(extractedHooks, HOOK_EVENTS.HTTP_BEFORE_REQUEST, {}, mockOptions);
expect(HooksRuntime).toHaveBeenCalled();
});
it('should handle errors gracefully', async () => {
const HooksRuntime = require('../src/runtime/hooks-runtime');
HooksRuntime.mockImplementationOnce(() => ({
runHooks: jest.fn().mockRejectedValue(new Error('Test error'))
}));
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
const result = await executeConsolidatedHooks(
{ collectionHooks: 'test()', folderHooks: [], requestHooks: '' },
HOOK_EVENTS.HTTP_BEFORE_REQUEST,
{},
mockOptions
);
expect(result).toBeNull();
consoleSpy.mockRestore();
});
});
describe('executeAllHookLevels()', () => {
it('should always use consolidated execution', async () => {
const extractedHooks = {
collectionHooks: 'collection()',
folderHooks: [{ hooks: 'folder()' }],
requestHooks: 'request()'
};
const HooksRuntime = require('../src/runtime/hooks-runtime');
await executeAllHookLevels(
extractedHooks,
HOOK_EVENTS.HTTP_BEFORE_REQUEST,
{},
mockOptions
);
// Should always use consolidated execution
const runtimeInstance = HooksRuntime.mock.results[0].value;
expect(runtimeInstance.runHooks).toHaveBeenCalledWith(
expect.objectContaining({ consolidated: true })
);
});
});
describe('createHookExecutor()', () => {
it('should create executor with base options', () => {
const executor = createHookExecutor(mockOptions);
expect(executor).toBeDefined();
expect(typeof executor.executeLevel).toBe('function');
expect(typeof executor.executeConsolidated).toBe('function');
expect(typeof executor.executeAll).toBe('function');
});
it('should allow overriding options', async () => {
const executor = createHookExecutor(mockOptions);
const newConsoleLog = jest.fn();
await executor.executeLevel('test()', HOOK_EVENTS.HTTP_BEFORE_REQUEST, {}, {
onConsoleLog: newConsoleLog
});
// The override should be merged with base options
// Actual verification depends on implementation details
});
});
});