feat: enhance hook functionality with runSingleRequestByPathname

- Introduced runSingleRequestByPathname to allow running requests at the collection level.
- Updated executeCollectionHooks to utilize the new function for improved hook execution.
- Added comprehensive tests for bru.runRequest in various hook scenarios, ensuring correct behavior and response handling.
- Removed redundant code and optimized hook management in collection.js.
This commit is contained in:
sanish-bruno
2026-01-22 20:13:10 +05:30
parent 2c474e8052
commit 3d319855bc
7 changed files with 213 additions and 95 deletions

View File

@@ -622,45 +622,7 @@ const handler = async function (argv) {
console[type](...args);
};
// Helper function to execute collection-level hooks at runtime
const executeCollectionHooks = async (hookEvent, eventData) => {
collectionRoot = collection?.draft?.root || collection?.root || {};
const collectionHooks = get(collectionRoot, 'request.script.hooks', '');
if (!collectionHooks || !collectionHooks.trim()) {
return;
}
try {
const hooksRuntime = new HooksRuntime({ runtime: scriptingConfig?.runtime });
const result = await hooksRuntime.runHooks({
hooksFile: decomment(collectionHooks),
request: {}, // Placeholder request for collection-level hooks
envVariables: envVars,
runtimeVariables,
collectionPath,
onConsoleLog,
processEnvVars,
scriptingConfig,
runRequestByItemPathname: null, // Not available at collection level
collectionName
});
if (result?.hookManager) {
await result.hookManager.call(hookEvent, eventData);
// Dispose HookManager to free VM resources
if (result.hookManager && typeof result.hookManager.dispose === 'function') {
result.hookManager.dispose();
}
}
} catch (error) {
console.error(`Error executing collection-level hooks for ${hookEvent}:`, error);
}
};
// Call onBeforeCollectionRun hook before starting to run requests
await executeCollectionHooks(HOOK_EVENTS.RUNNER_BEFORE_COLLECTION_RUN, { collection });
// Define runSingleRequestByPathname before executeCollectionHooks so it's available at all hook levels
const runSingleRequestByPathname = async (relativeItemPathname) => {
const ext = FORMAT_CONFIG[collection.format].ext;
return new Promise(async (resolve, reject) => {
@@ -688,6 +650,45 @@ const handler = async function (argv) {
});
};
// Helper function to execute collection-level hooks at runtime
const executeCollectionHooks = async (hookEvent, eventData) => {
collectionRoot = collection?.draft?.root || collection?.root || {};
const collectionHooks = get(collectionRoot, 'request.script.hooks', '');
if (!collectionHooks || !collectionHooks.trim()) {
return;
}
try {
const hooksRuntime = new HooksRuntime({ runtime: scriptingConfig?.runtime });
const result = await hooksRuntime.runHooks({
hooksFile: decomment(collectionHooks),
request: {}, // Placeholder request for collection-level hooks
envVariables: envVars,
runtimeVariables,
collectionPath,
onConsoleLog,
processEnvVars,
scriptingConfig,
runRequestByItemPathname: runSingleRequestByPathname,
collectionName
});
if (result?.hookManager) {
await result.hookManager.call(hookEvent, eventData);
// Dispose HookManager to free VM resources
if (result.hookManager && typeof result.hookManager.dispose === 'function') {
result.hookManager.dispose();
}
}
} catch (error) {
console.error(`Error executing collection-level hooks for ${hookEvent}:`, error);
}
};
// Call onBeforeCollectionRun hook before starting to run requests
await executeCollectionHooks(HOOK_EVENTS.RUNNER_BEFORE_COLLECTION_RUN, { collection });
let currentRequestIndex = 0;
let nJumps = 0; // count the number of jumps to avoid infinite loops
while (currentRequestIndex < requestItems.length) {

View File

@@ -429,60 +429,6 @@ const HOOK_EVENTS = Object.freeze({
RUNNER_AFTER_COLLECTION_RUN: 'runner:afterCollectionRun'
});
/**
* Get or create HookManager for a specific level (collection, folder, or request)
* @param {Map} hookManagersMap - Map storing HookManagers by key
* @param {string} key - Unique identifier (collection:${pathname}, folder:${pathname}, or request uid/pathname)
* @param {string} hooksFile - Hooks file content for this level
* @param {object} options - Options for hook registration
* @param {object} options.request - Request object
* @param {object} options.envVars - Environment variables (or envVariables)
* @param {object} options.runtimeVariables - Runtime variables
* @param {string} options.collectionPath - Collection path
* @param {function} options.onConsoleLog - Console log callback
* @param {object} options.processEnvVars - Process environment variables
* @param {object} options.scriptingConfig - Scripting configuration
* @param {function} options.runRequestByItemPathname - Function to run requests
* @param {string} options.collectionName - Collection name
* @returns {Promise<HookManager>} HookManager instance for this level
*/
const getOrCreateHookManager = async (hookManagersMap, key, hooksFile, options = {}) => {
// Return existing HookManager if already created
if (hookManagersMap.has(key)) {
return hookManagersMap.get(key);
}
// Create new HookManager and register hooks
const hookManager = new HookManager();
hookManagersMap.set(key, hookManager);
if (hooksFile && hooksFile.trim()) {
const hooksRuntime = new HooksRuntime({ runtime: options.scriptingConfig?.runtime });
try {
await hooksRuntime.runHooks({
hooksFile: decomment(hooksFile),
hookManager,
request: options.request || {},
envVariables: options.envVars || options.envVariables || {},
runtimeVariables: options.runtimeVariables || {},
collectionPath: options.collectionPath,
onConsoleLog: options.onConsoleLog,
processEnvVars: options.processEnvVars || {},
scriptingConfig: options.scriptingConfig || {},
runRequestByItemPathname: options.runRequestByItemPathname,
collectionName: options.collectionName
});
} catch (error) {
console.error(`Error registering hooks for ${key}:`, error);
if (options.onConsoleLog) {
options.onConsoleLog('error', [`Error registering hooks for ${key}: ${error.message}`]);
}
}
}
return hookManager;
};
const getAllRequestsInFolder = (folderItems = [], recursive = true) => {
let requests = [];
@@ -714,6 +660,5 @@ module.exports = {
getAllRequestsAtFolderRoot,
getCallStack,
extractHooks,
HOOK_EVENTS,
getOrCreateHookManager
HOOK_EVENTS
};