mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-15 11:51:30 +00:00
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:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -68,6 +68,32 @@ script:hooks {
|
||||
console.log('[onBeforeCollectionRun] Setup complete - token and counters initialized');
|
||||
});
|
||||
|
||||
// Test: bru.runRequest in onBeforeCollectionRun hook
|
||||
bru.hooks.runner.onBeforeCollectionRun(async() => {
|
||||
console.log('[onBeforeCollectionRun] Testing bru.runRequest in collection-level hook...');
|
||||
|
||||
try {
|
||||
// Call another request using bru.runRequest
|
||||
const response = await bru.runRequest('hooks/bru-run-request/helper-request');
|
||||
|
||||
// Store the response for verification in tests
|
||||
if (response) {
|
||||
bru.setVar('collection-run-request-response', response.data);
|
||||
bru.setVar('collection-run-request-status', response.status);
|
||||
bru.setVar('collection-run-request-tested', 'true');
|
||||
console.log('[onBeforeCollectionRun] bru.runRequest succeeded:', response.data);
|
||||
} else {
|
||||
bru.setVar('collection-run-request-error', 'No response received');
|
||||
console.log('[onBeforeCollectionRun] bru.runRequest failed - no response');
|
||||
}
|
||||
} catch (error) {
|
||||
bru.setVar('collection-run-request-error', error.message || String(error));
|
||||
console.log('[onBeforeCollectionRun] bru.runRequest error:', error);
|
||||
}
|
||||
|
||||
console.log('[onBeforeCollectionRun] bru.runRequest test completed');
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// onAfterCollectionRun Hook Tests
|
||||
// ============================================
|
||||
@@ -136,6 +162,10 @@ script:hooks {
|
||||
bru.deleteVar('success-count');
|
||||
bru.deleteVar('final-stats');
|
||||
bru.deleteVar('cleanup-performed');
|
||||
bru.deleteVar('collection-run-request-response');
|
||||
bru.deleteVar('collection-run-request-status');
|
||||
bru.deleteVar('collection-run-request-tested');
|
||||
bru.deleteVar('collection-run-request-error');
|
||||
|
||||
await bru.sleep(300);
|
||||
console.log('[onAfterCollectionRun] Cleanup complete - all test variables removed');
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
meta {
|
||||
name: helper-request
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{host}}/ping
|
||||
body: none
|
||||
auth: none
|
||||
}
|
||||
|
||||
tests {
|
||||
test("helper request returns pong", function() {
|
||||
expect(res.getBody()).to.equal('pong');
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
meta {
|
||||
name: run-request-in-after-hook
|
||||
type: http
|
||||
seq: 3
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{host}}/ping
|
||||
body: none
|
||||
auth: none
|
||||
}
|
||||
|
||||
script:hooks {
|
||||
bru.hooks.http.onAfterResponse(async ({ res }) => {
|
||||
console.log('[afterResponse] Testing bru.runRequest API');
|
||||
console.log('[afterResponse] Main request status:', res.getStatus());
|
||||
|
||||
// Call another request using bru.runRequest
|
||||
const response = await bru.runRequest('hooks/bru-run-request/helper-request');
|
||||
|
||||
// Store the response body for verification in tests
|
||||
if (response) {
|
||||
bru.setVar('after-helper-response-body', response.data);
|
||||
bru.setVar('after-helper-response-status', response.status);
|
||||
console.log('[afterResponse] Helper request response:', response.data);
|
||||
} else {
|
||||
bru.setVar('after-helper-response-error', 'No response received');
|
||||
console.log('[afterResponse] Helper request failed - no response');
|
||||
}
|
||||
|
||||
console.log('[afterResponse] bru.runRequest test completed');
|
||||
});
|
||||
}
|
||||
|
||||
tests {
|
||||
test("bru.runRequest in afterResponse hook should return helper response", function() {
|
||||
const helperBody = bru.getVar('after-helper-response-body');
|
||||
expect(helperBody).to.equal('pong');
|
||||
});
|
||||
|
||||
test("bru.runRequest in afterResponse hook should return status 200", function() {
|
||||
const helperStatus = bru.getVar('after-helper-response-status');
|
||||
expect(helperStatus).to.equal(200);
|
||||
});
|
||||
|
||||
test("main request should succeed", function() {
|
||||
expect(res.getBody()).to.equal('pong');
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
meta {
|
||||
name: run-request-in-before-hook
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{host}}/ping
|
||||
body: none
|
||||
auth: none
|
||||
}
|
||||
|
||||
script:hooks {
|
||||
bru.hooks.http.onBeforeRequest(async ({ req }) => {
|
||||
console.log('[beforeRequest] Testing bru.runRequest API');
|
||||
|
||||
// Call another request using bru.runRequest
|
||||
const response = await bru.runRequest('hooks/bru-run-request/helper-request');
|
||||
|
||||
// Store the response body for verification in tests
|
||||
if (response) {
|
||||
bru.setVar('helper-response-body', response.data);
|
||||
bru.setVar('helper-response-status', response.status);
|
||||
console.log('[beforeRequest] Helper request response:', response.data);
|
||||
} else {
|
||||
bru.setVar('helper-response-error', 'No response received');
|
||||
console.log('[beforeRequest] Helper request failed - no response');
|
||||
}
|
||||
|
||||
console.log('[beforeRequest] bru.runRequest test completed');
|
||||
});
|
||||
}
|
||||
|
||||
tests {
|
||||
test("bru.runRequest in beforeRequest hook should return helper response", function() {
|
||||
const helperBody = bru.getVar('helper-response-body');
|
||||
expect(helperBody).to.equal('pong');
|
||||
});
|
||||
|
||||
test("bru.runRequest in beforeRequest hook should return status 200", function() {
|
||||
const helperStatus = bru.getVar('helper-response-status');
|
||||
expect(helperStatus).to.equal(200);
|
||||
});
|
||||
|
||||
test("main request should still succeed", function() {
|
||||
expect(res.getBody()).to.equal('pong');
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
meta {
|
||||
name: verify-collection-run-request
|
||||
type: http
|
||||
seq: 4
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{host}}/ping
|
||||
body: none
|
||||
auth: none
|
||||
}
|
||||
|
||||
tests {
|
||||
test("collection-level bru.runRequest should have set response body", function() {
|
||||
const responseBody = bru.getVar('collection-run-request-response');
|
||||
expect(responseBody).to.equal('pong');
|
||||
});
|
||||
|
||||
test("collection-level bru.runRequest should have set response status", function() {
|
||||
const responseStatus = bru.getVar('collection-run-request-status');
|
||||
expect(responseStatus).to.equal(200);
|
||||
});
|
||||
|
||||
test("collection-level bru.runRequest flag should be set", function() {
|
||||
const flag = bru.getVar('collection-run-request-tested');
|
||||
expect(flag).to.equal('true');
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user