From 7b0f0853095bcb08eebc9a6720b433fc65276411 Mon Sep 17 00:00:00 2001 From: barshan23 Date: Sat, 23 Aug 2025 14:01:44 +0530 Subject: [PATCH 1/6] [AB-1354] Add support for nested folder organization using tags in OpenAPI tree generation --- lib/options.js | 12 + .../generateSkeletionTreeFromOpenAPI.js | 270 ++++++++---------- 2 files changed, 138 insertions(+), 144 deletions(-) diff --git a/lib/options.js b/lib/options.js index e3cdc2c56..374e39a54 100644 --- a/lib/options.js +++ b/lib/options.js @@ -180,6 +180,18 @@ module.exports = { supportedIn: [VERSION20, VERSION30, VERSION31], supportedModuleVersion: [MODULE_VERSION.V2, MODULE_VERSION.V1] }, + { + name: 'Nested folder organization using tags', + id: 'tagsFolderHierarchy', + type: 'enum', + default: 'Flat', + availableOptions: ['Nested', 'Flat'], + description: 'Select whether to create heirarchical folders according to the spec’s tags order.', + external: false, + usage: ['CONVERSION'], + supportedIn: [VERSION20, VERSION30, VERSION31], + supportedModuleVersion: [MODULE_VERSION.V2] + }, { name: 'Enable Schema Faking', id: 'schemaFaker', diff --git a/libV2/helpers/collection/generateSkeletionTreeFromOpenAPI.js b/libV2/helpers/collection/generateSkeletionTreeFromOpenAPI.js index 9c64ace27..d18a33eee 100644 --- a/libV2/helpers/collection/generateSkeletionTreeFromOpenAPI.js +++ b/libV2/helpers/collection/generateSkeletionTreeFromOpenAPI.js @@ -176,148 +176,6 @@ let _ = require('lodash'), return tree; }, - // _generateTreeFromPaths = function (openapi, { includeDeprecated }) { - // /** - // * We will create a unidirectional graph - // */ - // let tree = new Graph(); - - // tree.setNode('root:collection', { - // type: 'collection', - // data: {}, - // meta: {} - // }); - - // _.forEach(openapi.paths, function (methods, path) { - // let pathSplit = path === '/' ? [path] : _.compact(path.split('/')); - - // // if after path split we just have one entry - // // that means no folders need to be generated. - // // check for all the methods inside it and expand. - // if (pathSplit.length === 1) { - // /** - // * Always first try to find the node if it already exists. - // * if yes, bail out nothing is needed to be done. - // * - // * if the path length is 1, then also generate - // * the folder otherwise /pet and /pet/:id will never be in same folder. - // */ - // // if (!tree.hasNode(`path:${pathSplit[0]}`)) { - // // tree.setNode(`path:${pathSplit[0]}`, { - // // type: 'folder', - // // meta: { - // // name: pathSplit[0], - // // path: pathSplit[0], - // // pathIdentifier: pathIdentifier - // // }, - // // data: {} - // // }); - - // // tree.setEdge('root:collection', `path:${pathSplit[0]}`); - // // } - - - // _.forEach(methods, function (data, method) { - // if (!ALLOWED_HTTP_METHODS[method]) { - // return; - // } - - // /** - // * include deprecated handling. - // * If true, add in the postman collection. If false ignore the request. - // */ - // if (!includeDeprecated && data.deprecated) { - // return; - // } - - // tree.setNode(`path:${pathSplit[0]}:${method}`, { - // type: 'request', - // meta: { - // path: path, - // method: method, - // pathIdentifier: pathSplit[0] - // }, - // data: {} - // }); - - // tree.setEdge(`path:${pathSplit[0]}`, `path:${pathSplit[0]}:${method}`); - // }); - // } - - // else { - // _.forEach(pathSplit, function (p, index) { - // let previousPathIdentified = pathSplit.slice(0, index).join('/'), - // pathIdentifier = pathSplit.slice(0, index + 1).join('/'); - - // /** - // * Always first try to find the node if it already exists. - // * if yes, bail out nothing is needed to be done. - // */ - // if (tree.hasNode(`path:${pathIdentifier}`)) { - // return; - // } - - // else { - // tree.setNode(`path:${pathIdentifier}`, { - // type: 'folder', - // meta: { - // name: p, - // path: p, - // pathIdentifier: pathIdentifier - // }, - // data: {} - // }); - - // /** - // * If index is 0, this means that we are on the first level. - // * Hence it is folder/request to be added on the first level - // * - // * If after the split we have more than one paths, then we need - // * to add to the previous node. - // */ - // tree.setEdge(index === 0 ? 'root:collection' : `path:${previousPathIdentified}`, `path:${pathIdentifier}`); - // } - // }); - - // /** - // * Now for all the methods present in the path, add the request nodes. - // */ - - // _.forEach(methods, function (data, method) { - // if (!ALLOWED_HTTP_METHODS[method]) { - // return; - // } - - // /** - // * include deprecated handling. - // * If true, add in the postman collection. If false ignore the request. - // */ - // if (!includeDeprecated && data.deprecated) { - // return; - // } - - // // join till the last path i.e. the folder. - // let previousPathIdentified = pathSplit.slice(0, (pathSplit.length)).join('/'), - // pathIdentifier = `${pathSplit.join('/')}:${method}`; - - // tree.setNode(`path:${pathIdentifier}`, { - // type: 'request', - // data: {}, - // meta: { - // path: path, - // method: method, - // pathIdentifier: pathIdentifier - // } - // }); - - // tree.setEdge(`path:${previousPathIdentified}`, `path:${pathIdentifier}`); - // }); - // } - // }); - - // return tree; - // }, - _generateTreeFromTags = function (openapi, { includeDeprecated }) { let tree = new Graph(), @@ -424,6 +282,124 @@ let _ = require('lodash'), return tree; }, + /** + * Generates tree structure with nested folders based on tag order + * @param {Object} openapi - OpenAPI specification + * @param {Object} options - Generation options + * @param {boolean} options.includeDeprecated - Whether to include deprecated operations + * @returns {Object} - Graph tree with nested folder structure + */ + _generateTreeFromNestedTags = function (openapi, { includeDeprecated }) { + let tree = new Graph(), + + tagDescMap = _.reduce(openapi.tags, function (acc, data) { + acc[data.name] = data.description; + + return acc; + }, {}); + + tree.setNode('root:collection', { + type: 'collection', + data: {}, + meta: {} + }); + + /** + * Helper function to create nested folder structure for tags + * @param {Array} tags - Array of tags to create nested folders for + * @returns {String} - Node ID of the deepest folder created + */ + const createNestedFolders = function (tags) { + if (!tags || tags.length === 0) { + return 'root:collection'; + } + + let parentNodeId = 'root:collection'; + + // Create nested folder structure based on tag order + for (let i = 0; i < tags.length; i++) { + const tag = tags[i], + folderPath = tags.slice(0, i + 1).join(':'), + currentNodeId = `path:${folderPath}`; + + // Create folder node if it doesn't exist + if (!tree.hasNode(currentNodeId)) { + tree.setNode(currentNodeId, { + type: 'folder', + meta: { + path: '', + name: tag, + description: tagDescMap[tag] || '' + }, + data: {} + }); + + // Connect to parent (either root collection or previous folder) + tree.setEdge(parentNodeId, currentNodeId); + } + + parentNodeId = currentNodeId; + } + + return parentNodeId; + }; + + _.forEach(openapi.paths, function (methods, path) { + _.forEach(methods, function (data, method) { + if (!ALLOWED_HTTP_METHODS[method]) { + return; + } + + /** + * include deprecated handling. + * If true, add in the postman collection. If false ignore the request. + */ + if (!includeDeprecated && data.deprecated) { + return; + } + + /** + * Create nested folder structure based on tags order + * and place the request in the deepest folder + */ + if (data.tags && data.tags.length > 0) { + // Create nested folder structure and get the deepest folder node + const deepestFolderNodeId = createNestedFolders(data.tags), + // Create a unique request node ID (one per operation) + requestNodeId = `request:${path}:${method}`; + + tree.setNode(requestNodeId, { + type: 'request', + data: {}, + meta: { + tags: data.tags, + path: path, + method: method + } + }); + + // Connect request to the deepest folder + tree.setEdge(deepestFolderNodeId, requestNodeId); + } + else { + // No tags - place request directly under root collection + tree.setNode(`path:${path}:${method}`, { + type: 'request', + data: {}, + meta: { + path: path, + method: method + } + }); + + tree.setEdge('root:collection', `path:${path}:${method}`); + } + }); + }); + + return tree; + }, + _generateWebhookEndpoints = function (openapi, tree, { includeDeprecated }) { if (!_.isEmpty(openapi.webhooks)) { tree.setNode(`${PATH_WEBHOOK}:folder`, { @@ -470,12 +446,18 @@ let _ = require('lodash'), * * @returns {Object} - tree format */ -module.exports = function (openapi, { folderStrategy, includeWebhooks, includeDeprecated }) { +module.exports = function (openapi, { folderStrategy, includeWebhooks, includeDeprecated, tagsFolderHierarchy }) { let skeletonTree; switch (folderStrategy) { case 'tags': - skeletonTree = _generateTreeFromTags(openapi, { includeDeprecated }); + if (tagsFolderHierarchy === 'flat') { + skeletonTree = _generateTreeFromTags(openapi, { includeDeprecated }); + } + else { + skeletonTree = _generateTreeFromNestedTags(openapi, { includeDeprecated }); + } + break; case 'paths': From e840c89b0160a5c84724ecfc3f76ebdcf339a9f8 Mon Sep 17 00:00:00 2001 From: barshan23 Date: Mon, 25 Aug 2025 17:16:44 +0530 Subject: [PATCH 2/6] Added the new folder structure related options in the readme and test --- OPTIONS.md | 1 + test/system/structure.test.js | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/OPTIONS.md b/OPTIONS.md index 1f9565549..fc4e1103a 100644 --- a/OPTIONS.md +++ b/OPTIONS.md @@ -27,4 +27,5 @@ includeWebhooks|boolean|-|false|Select whether to include Webhooks in the genera includeReferenceMap|boolean|-|false|Whether or not to include reference map or not as part of output|BUNDLE|v2, v1 includeDeprecated|boolean|-|true|Select whether to include deprecated operations, parameters, and properties in generated collection or not|CONVERSION, VALIDATION|v2, v1 alwaysInheritAuthentication|boolean|-|false|Whether authentication details should be included on every request, or always inherited from the collection.|CONVERSION|v2, v1 +tagsFolderHierarchy|enum|Nested, Flat|Flat|Select whether to create heirarchical folders according to the spec’s tags order.|CONVERSION|v2 preferredRequestBodyType|enum|x-www-form-urlencoded, form-data, raw, first-listed|first-listed|When there are multiple content-types defined in the request body of OpenAPI, the conversion selects the preferred option content-type as request body.If "first-listed" is set, the first content-type defined in the OpenAPI spec will be selected.|CONVERSION|v2 diff --git a/test/system/structure.test.js b/test/system/structure.test.js index f2bbed18e..593319365 100644 --- a/test/system/structure.test.js +++ b/test/system/structure.test.js @@ -8,6 +8,7 @@ const optionIds = [ 'requestParametersResolution', 'exampleParametersResolution', 'folderStrategy', + 'tagsFolderHierarchy', 'indentCharacter', 'requestNameSource', 'schemaFaker', @@ -68,6 +69,13 @@ const optionIds = [ availableOptions: ['Paths', 'Tags'], description: 'Select whether to create folders according to the spec’s paths or tags.' }, + tagsFolderHierarchy: { + name: 'Nested folder organization using tags', + type: 'enum', + default: 'Flat', + availableOptions: ['Nested', 'Flat'], + description: 'Select whether to create heirarchical folders according to the spec’s tags order.' + }, optimizeConversion: { name: 'Optimize conversion', id: 'optimizeConversion', From dfa9e6bc238514a6b0359c68243aca1b5f421784 Mon Sep 17 00:00:00 2001 From: barshan23 Date: Tue, 26 Aug 2025 16:22:41 +0530 Subject: [PATCH 3/6] Refactor folder hierarchy option to use boolean for nested organization in OpenAPI conversion --- OPTIONS.md | 2 +- lib/options.js | 9 ++++----- .../collection/generateSkeletionTreeFromOpenAPI.js | 8 ++++---- test/system/structure.test.js | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/OPTIONS.md b/OPTIONS.md index fc4e1103a..fd7c2e15f 100644 --- a/OPTIONS.md +++ b/OPTIONS.md @@ -27,5 +27,5 @@ includeWebhooks|boolean|-|false|Select whether to include Webhooks in the genera includeReferenceMap|boolean|-|false|Whether or not to include reference map or not as part of output|BUNDLE|v2, v1 includeDeprecated|boolean|-|true|Select whether to include deprecated operations, parameters, and properties in generated collection or not|CONVERSION, VALIDATION|v2, v1 alwaysInheritAuthentication|boolean|-|false|Whether authentication details should be included on every request, or always inherited from the collection.|CONVERSION|v2, v1 -tagsFolderHierarchy|enum|Nested, Flat|Flat|Select whether to create heirarchical folders according to the spec’s tags order.|CONVERSION|v2 +nestedFolderHierarchy|boolean|-|false|Select whether to create heirarchical folders according to the spec's tags order.|CONVERSION|v2 preferredRequestBodyType|enum|x-www-form-urlencoded, form-data, raw, first-listed|first-listed|When there are multiple content-types defined in the request body of OpenAPI, the conversion selects the preferred option content-type as request body.If "first-listed" is set, the first content-type defined in the OpenAPI spec will be selected.|CONVERSION|v2 diff --git a/lib/options.js b/lib/options.js index 374e39a54..6472e5474 100644 --- a/lib/options.js +++ b/lib/options.js @@ -182,11 +182,10 @@ module.exports = { }, { name: 'Nested folder organization using tags', - id: 'tagsFolderHierarchy', - type: 'enum', - default: 'Flat', - availableOptions: ['Nested', 'Flat'], - description: 'Select whether to create heirarchical folders according to the spec’s tags order.', + id: 'nestedFolderHierarchy', + type: 'boolean', + default: false, + description: 'Select whether to create heirarchical folders according to the spec\'s tags order.', external: false, usage: ['CONVERSION'], supportedIn: [VERSION20, VERSION30, VERSION31], diff --git a/libV2/helpers/collection/generateSkeletionTreeFromOpenAPI.js b/libV2/helpers/collection/generateSkeletionTreeFromOpenAPI.js index d18a33eee..69d49898f 100644 --- a/libV2/helpers/collection/generateSkeletionTreeFromOpenAPI.js +++ b/libV2/helpers/collection/generateSkeletionTreeFromOpenAPI.js @@ -446,16 +446,16 @@ let _ = require('lodash'), * * @returns {Object} - tree format */ -module.exports = function (openapi, { folderStrategy, includeWebhooks, includeDeprecated, tagsFolderHierarchy }) { +module.exports = function (openapi, { folderStrategy, includeWebhooks, includeDeprecated, nestedFolderHierarchy }) { let skeletonTree; switch (folderStrategy) { case 'tags': - if (tagsFolderHierarchy === 'flat') { - skeletonTree = _generateTreeFromTags(openapi, { includeDeprecated }); + if (nestedFolderHierarchy) { + skeletonTree = _generateTreeFromNestedTags(openapi, { includeDeprecated }); } else { - skeletonTree = _generateTreeFromNestedTags(openapi, { includeDeprecated }); + skeletonTree = _generateTreeFromTags(openapi, { includeDeprecated }); } break; diff --git a/test/system/structure.test.js b/test/system/structure.test.js index 593319365..dd8f4617e 100644 --- a/test/system/structure.test.js +++ b/test/system/structure.test.js @@ -8,7 +8,7 @@ const optionIds = [ 'requestParametersResolution', 'exampleParametersResolution', 'folderStrategy', - 'tagsFolderHierarchy', + 'nestedFolderHierarchy', 'indentCharacter', 'requestNameSource', 'schemaFaker', From 39e0df81f56d96e4e1252f029f0ac1eb680f5dd4 Mon Sep 17 00:00:00 2001 From: barshan23 Date: Tue, 26 Aug 2025 16:28:41 +0530 Subject: [PATCH 4/6] Update external visibility for hierarchical folder creation option in getOptions --- lib/options.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/options.js b/lib/options.js index 6472e5474..c13149297 100644 --- a/lib/options.js +++ b/lib/options.js @@ -186,7 +186,7 @@ module.exports = { type: 'boolean', default: false, description: 'Select whether to create heirarchical folders according to the spec\'s tags order.', - external: false, + external: true, usage: ['CONVERSION'], supportedIn: [VERSION20, VERSION30, VERSION31], supportedModuleVersion: [MODULE_VERSION.V2] From de916300bf758c6c23f03354781ee2d15b83adb2 Mon Sep 17 00:00:00 2001 From: barshan23 Date: Tue, 26 Aug 2025 19:00:39 +0530 Subject: [PATCH 5/6] Update description for nested folder hierarchy option to clarify functionality --- OPTIONS.md | 2 +- lib/options.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OPTIONS.md b/OPTIONS.md index fd7c2e15f..f142b030e 100644 --- a/OPTIONS.md +++ b/OPTIONS.md @@ -27,5 +27,5 @@ includeWebhooks|boolean|-|false|Select whether to include Webhooks in the genera includeReferenceMap|boolean|-|false|Whether or not to include reference map or not as part of output|BUNDLE|v2, v1 includeDeprecated|boolean|-|true|Select whether to include deprecated operations, parameters, and properties in generated collection or not|CONVERSION, VALIDATION|v2, v1 alwaysInheritAuthentication|boolean|-|false|Whether authentication details should be included on every request, or always inherited from the collection.|CONVERSION|v2, v1 -nestedFolderHierarchy|boolean|-|false|Select whether to create heirarchical folders according to the spec's tags order.|CONVERSION|v2 +nestedFolderHierarchy|boolean|-|false|Enable this option to create subfolders in the collection based on the order of tags.|CONVERSION|v2 preferredRequestBodyType|enum|x-www-form-urlencoded, form-data, raw, first-listed|first-listed|When there are multiple content-types defined in the request body of OpenAPI, the conversion selects the preferred option content-type as request body.If "first-listed" is set, the first content-type defined in the OpenAPI spec will be selected.|CONVERSION|v2 diff --git a/lib/options.js b/lib/options.js index c13149297..7a424c8b4 100644 --- a/lib/options.js +++ b/lib/options.js @@ -185,7 +185,7 @@ module.exports = { id: 'nestedFolderHierarchy', type: 'boolean', default: false, - description: 'Select whether to create heirarchical folders according to the spec\'s tags order.', + description: 'Enable this option to create subfolders in the collection based on the order of tags.', external: true, usage: ['CONVERSION'], supportedIn: [VERSION20, VERSION30, VERSION31], From 50d2c94c0182eb4d47af10cd04c5141b985dff56 Mon Sep 17 00:00:00 2001 From: barshan23 Date: Thu, 28 Aug 2025 15:12:52 +0530 Subject: [PATCH 6/6] Add test for nested folder generation using tags in OpenAPI conversion --- .../valid_openapi/spacecraft_nested_tags.json | 286 ++++++++++++++++++ test/unit/convertV2.test.js | 35 +++ 2 files changed, 321 insertions(+) create mode 100644 test/data/valid_openapi/spacecraft_nested_tags.json diff --git a/test/data/valid_openapi/spacecraft_nested_tags.json b/test/data/valid_openapi/spacecraft_nested_tags.json new file mode 100644 index 000000000..15a63362b --- /dev/null +++ b/test/data/valid_openapi/spacecraft_nested_tags.json @@ -0,0 +1,286 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "help me please", + "version": "1.0.0", + "description": "Buy or rent spacecrafts updated to new content\n\nContact Support:\n Name: Jon" + }, + "servers": [ + { + "url": "{{baseUrl}}" + } + ], + "paths": { + "/spacecraft/{spacecraftId}/help/{helpme}/{help}/{user-id+v1+special}": { + "parameters": [ + { + "name": "spacecraftId", + "in": "path", + "required": true, + "description": "(Required) ", + "example": "asdfadsfasf", + "schema": { + "deprecated": false, + "type": "string" + } + }, + { + "name": "helpme", + "in": "path", + "required": true, + "description": "(Required) ", + "example": "{{aDSASD}}", + "schema": { + "deprecated": false, + "type": "string" + } + }, + { + "name": "help", + "in": "path", + "required": true, + "description": "(Required) ", + "example": "{{help}}", + "schema": { + "deprecated": false, + "type": "string" + } + }, + { + "name": "user-id+v1+special", + "in": "path", + "required": true, + "example": "1", + "schema": { + "type": "integer" + } + } + ], + "get": { + "summary": "Read a spacecraft", + "tags": [ + "spacecraft", + "help" + ], + "parameters": [ + { + "name": "Accept", + "in": "header", + "required": false, + "example": "application/json", + "schema": { + "deprecated": false + } + } + ], + "responses": { + "200": { + "headers": { + "Content-Type": { + "schema": { + "deprecated": false, + "type": "string" + }, + "example": "application/json" + } + }, + "description": "The spacecraft corresponding to the provided `spacecraftId`", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "example": { + "id": "string", + "name": "string", + "type": "string", + "description": "string" + } + } + } + }, + "404": { + "headers": { + "Content-Type": { + "schema": { + "deprecated": false, + "type": "string" + }, + "example": "application/json" + } + }, + "description": "No spacecraft found for the provided `spacecraftId`", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "example": { + "message": "string" + } + } + } + }, + "500": { + "headers": { + "Content-Type": { + "schema": { + "deprecated": false, + "type": "string" + }, + "example": "application/json" + } + }, + "description": "Unexpected error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "example": { + "message": "string" + } + } + } + } + } + }, + "post": { + "summary": "Create a spacecraft", + "tags": [ + "spacecraft" + ], + "parameters": [ + { + "name": "Accept", + "in": "header", + "required": false, + "example": "application/json", + "schema": { + "deprecated": false + } + } + ], + "responses": { + "200": { + "headers": { + "Content-Type": { + "schema": { + "deprecated": false, + "type": "string" + }, + "example": "application/json" + } + }, + "description": "The spacecraft corresponding to the provided `spacecraftId`", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "example": { + "id": "string", + "name": "string", + "type": "string", + "description": "string" + } + } + } + }, + "404": { + "headers": { + "Content-Type": { + "schema": { + "deprecated": false, + "type": "string" + }, + "example": "application/json" + } + }, + "description": "No spacecraft found for the provided `spacecraftId`", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "example": { + "message": "string" + } + } + } + }, + "500": { + "headers": { + "Content-Type": { + "schema": { + "deprecated": false, + "type": "string" + }, + "example": "application/json" + } + }, + "description": "Unexpected error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "example": { + "message": "string" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/test/unit/convertV2.test.js b/test/unit/convertV2.test.js index 98038d10b..d3c9453b8 100644 --- a/test/unit/convertV2.test.js +++ b/test/unit/convertV2.test.js @@ -1842,6 +1842,41 @@ describe('The convert v2 Function', function() { }); }); + it('Should convert using tags as folder strategy and generated nested folder', function () { + const someOperationDeprecatedUsingTags = + path.join(__dirname, VALID_OPENAPI_PATH, '/spacecraft_nested_tags.json'), + fileData = fs.readFileSync(someOperationDeprecatedUsingTags, 'utf8'); + Converter.convertV2({ type: 'string', data: fileData }, + { folderStrategy: 'tags', nestedFolderHierarchy: true }, + (err, result) => { + expect(err).to.be.null; + expect(result.result).to.be.true; + expect(result.output[0].data.item.length).to.equal(1); + expect(result.output[0].data.item[0].name).to.equal('spacecraft'); + expect(result.output[0].data.item[0].item.length).to.equal(2); + expect(result.output[0].data.item[0].item[0].name).to.equal('help'); + expect(result.output[0].data.item[0].item[0].item.length).to.equal(1); + expect(result.output[0].data.item[0].item[0].item[0].name).to.equal('Read a spacecraft'); + expect(result.output[0].data.item[0].item[0].item[0]).to.have.keys( + 'event', + 'id', + 'name', + 'protocolProfileBehavior', + 'request', + 'response' + ); + expect(result.output[0].data.item[0].item[1].name).to.equal('Create a spacecraft'); + expect(result.output[0].data.item[0].item[1]).to.have.keys( + 'event', + 'id', + 'name', + 'protocolProfileBehavior', + 'request', + 'response' + ); + }); + }); + describe('[Github #57] - folderStrategy option (value: Tags) ' + tagsFolderSpec, function() { async.series({ pathsOutput: (cb) => {