From 8d517ac8f50f64be79f7eb1d058c1f894c100a6c Mon Sep 17 00:00:00 2001 From: Honza Javorek Date: Mon, 11 Mar 2019 14:50:34 +0100 Subject: [PATCH] refactor: rename 'configuraton.data' to 'configuration.apiDescriptions' * feat: add 'apiDescriptionMediaType' to the transaction object Add 'apiDescriptionMediaType' to the transaction object, turn '.data' into '.apiDescriptions', update docs. * test: put back incorrectly removed tests * test: don't use locale for sorting * refactor: remove 'apiDescriptionMediaType' from the public interface Because Dredd takes whatever Fury returns as the first media type from the .detect() function, it always gets application/swagger+json for OpenAPI 2, even for YAML files. It's not an issue for Dredd itself, but it is an issue once it's propagated to the transaction object. --- docs/usage-js.rst | 84 ++++----- lib/Dredd.js | 87 +++------ lib/TransactionRunner.js | 11 +- lib/configuration.js | 36 +++- lib/handleRuntimeProblems.js | 17 +- lib/reporters/ApiaryReporter.js | 13 +- lib/reporters/BaseReporter.js | 2 +- lib/reporters/CLIReporter.js | 2 +- lib/reporters/DotReporter.js | 2 +- lib/reporters/HTMLReporter.js | 2 +- lib/reporters/MarkdownReporter.js | 2 +- lib/reporters/NyanReporter.js | 2 +- lib/reporters/XUnitReporter.js | 2 +- .../{blueprint-data.js => apiDescriptions.js} | 10 +- test/integration/annotations-test.js | 13 +- test/integration/sanitation-test.js | 2 +- test/unit/Dredd-test.js | 177 ++++++++++-------- test/unit/configuration-test.js | 95 ++++++++++ test/unit/handleRuntimeProblems-test.js | 6 +- test/unit/reporters/ApiaryReporter-test.js | 32 ++-- test/unit/transactionRunner-test.js | 38 ++-- 21 files changed, 365 insertions(+), 270 deletions(-) rename test/fixtures/{blueprint-data.js => apiDescriptions.js} (68%) diff --git a/docs/usage-js.rst b/docs/usage-js.rst index f1e36d464..f7d253c38 100644 --- a/docs/usage-js.rst +++ b/docs/usage-js.rst @@ -33,71 +33,55 @@ Let’s have a look at an example configuration first. (Please also see the :ref { server: 'http://127.0.0.1:3000/api', // your URL to API endpoint the tests will run against options: { - - 'path': [], // Required Array if Strings; filepaths to API description documents, can use glob wildcards - + path: [], // Required Array if Strings; filepaths to API description documents, can use glob wildcards 'dry-run': false, // Boolean, do not run any real HTTP transaction - 'names': false, // Boolean, Print Transaction names and finish, similar to dry-run - - 'loglevel': 'warning', // String, logging level (debug, warning, error, silent) - - 'only': [], // Array of Strings, run only transaction that match these names - - 'header': [], // Array of Strings, these strings are then added as headers (key:value) to every transaction - 'user': null, // String, Basic Auth credentials in the form username:password - - 'hookfiles': [], // Array of Strings, filepaths to files containing hooks (can use glob wildcards) - - 'reporter': ['dot', 'html'], // Array of possible reporters, see folder lib/reporters - - 'output': [], // Array of Strings, filepaths to files used for output of file-based reporters - + names: false, // Boolean, Print Transaction names and finish, similar to dry-run + loglevel: 'warning', // String, logging level (debug, warning, error, silent) + only: [], // Array of Strings, run only transaction that match these names + header: [], // Array of Strings, these strings are then added as headers (key:value) to every transaction + user: null, // String, Basic Auth credentials in the form username:password + hookfiles: [], // Array of Strings, filepaths to files containing hooks (can use glob wildcards) + reporter: ['dot', 'html'], // Array of possible reporters, see folder lib/reporters + output: [], // Array of Strings, filepaths to files used for output of file-based reporters 'inline-errors': false, // Boolean, If failures/errors are display immediately in Dredd run - - 'require': null, // String, When using nodejs hooks, require the given module before executing hooks - - 'color': true, + require: null, // String, When using nodejs hooks, require the given module before executing hooks + color: true, }, + emitter: new EventEmitter(), // listen to test progress, your own instance of EventEmitter + apiDescriptions: ['FORMAT: 1A\n# Sample API\n'] + } - 'emitter': EventEmitterInstance, // optional - listen to test progress, your own instance of EventEmitter +.. js:data:: configuration - 'data': { - 'path/to/file': '...' - } - } +.. js:attribute:: configuration.server -Properties ----------- + The HTTP(S) address of the API server to test against the API description(s). A valid URL is expected, e.g. ``http://127.0.0.1:8000`` -server (string) -~~~~~~~~~~~~~~~ + :type: string + :required: yes -Your choice of the API endpoint to test the API description against. It must be a valid URL (you can specify ``port``, ``path`` and http or https ``protocol``). +.. js:attribute:: configuration.options -options (object) -~~~~~~~~~~~~~~~~ + Because :js:attr:`configuration.options.path` array is required, you must specify options. You’ll end with errors otherwise. -Because ``options.path`` array is required, you must specify options. You’ll end with errors otherwise. + :type: object + :required: yes -.. _optionspath-object: +.. js:attribute:: configuration.options.path -options.path (object) -^^^^^^^^^^^^^^^^^^^^^ + Array of paths or URLs to API description documents. -**Required** Array of filepaths to API description documents. Or it can also be an URL to download the API description from internet via http(s) protocol. + :type: array + :required: yes -data (object) -^^^^^^^^^^^^^ +.. js:attribute:: configuration.emitter -**Optional** Object with keys as ``filename`` and value as ``blueprint``-code. + Listen to test progress by providing your own instance of `EventEmitter `__. -Useful when you don’t want to operate on top of filesystem and want to pass code of your API description as a string. You get the point. + :type: EventEmitter -.. code-block:: javascript +.. js:attribute:: configuration.apiDescriptions - { - 'data': { - './api-description.apib': 'FORMAT: 1A\n\n# My String API\n\nGET /url\n+ Response 200\n\n Some content', - './directory/another-api-description.apib': '# Another API\n\n## Group Machines\n\n### Machine [/machine]\n\n#### Read machine [GET]\n\n...' - } - } + API descriptions as strings. Useful when you don't want to operate on top of the filesystem. + + :type: array diff --git a/lib/Dredd.js b/lib/Dredd.js index 29ba4da2e..154173cfd 100644 --- a/lib/Dredd.js +++ b/lib/Dredd.js @@ -20,15 +20,11 @@ const PROXY_ENV_VARIABLES = ['HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY']; const FILE_DOWNLOAD_TIMEOUT = 5000; -function removeDuplicates(arr) { - return arr.reduce((alreadyProcessed, currentItem) => { - if (alreadyProcessed.indexOf(currentItem) === -1) { - return alreadyProcessed.concat(currentItem); - } - return alreadyProcessed; - }, []); +function unique(array) { + return Array.from(new Set(array)); } + class Dredd { constructor(config) { this.init(config); @@ -49,6 +45,7 @@ class Dredd { end: 0, duration: 0, }; + this.configuration.files = []; this.transactions = []; this.runner = new Runner(this.configuration); this.logger = logger; @@ -77,37 +74,7 @@ https://dredd.org/en/latest/how-it-works/#using-https-proxy } run(callback) { - this.configDataIsEmpty = true; - - if (!this.configuration.files) { this.configuration.files = []; } - if (!this.configuration.data) { this.configuration.data = {}; } - - const passedConfigData = {}; - - const object = this.configuration.data || {}; - for (const key of Object.keys(object || {})) { - const val = object[key]; - this.configDataIsEmpty = false; - if (typeof val === 'string') { - passedConfigData[key] = { - filename: key, - raw: val, - }; - } else if (typeof val === 'object' && val.raw && val.filename) { - passedConfigData[val.filename] = { - filename: val.filename, - raw: val.raw, - }; - } - } - - if (!this.configDataIsEmpty) { - this.configuration.data = passedConfigData; - } - - // Remove duplicate paths - this.configuration.options.path = removeDuplicates(this.configuration.options.path); - + // Take care of --require if (this.configuration.options.require) { let mod = this.configuration.options.require; const abs = fs.existsSync(mod) || fs.existsSync(`${mod}.js`); @@ -155,6 +122,8 @@ https://dredd.org/en/latest/how-it-works/#using-https-proxy } // Expand all globs + // TODO use the same mechanism as in 'resolveHookfiles', this is unnecessary, + // duplicate work expandGlobs(callback) { async.each(this.configuration.options.path, (globToExpand, globCallback) => { if (/^http(s)?:\/\//.test(globToExpand)) { @@ -178,7 +147,7 @@ https://dredd.org/en/latest/how-it-works/#using-https-proxy (err) => { if (err) { return callback(err, this.stats); } - if (this.configDataIsEmpty && this.configuration.files.length === 0) { + if (this.configuration.apiDescriptions.length === 0 && this.configuration.files.length === 0) { err = new Error(` API description document (or documents) not found on path: '${this.configuration.options.path}' @@ -187,7 +156,7 @@ API description document (or documents) not found on path: } // Remove duplicate filenames - this.configuration.files = removeDuplicates(this.configuration.files); + this.configuration.files = unique(this.configuration.files); callback(null, this.stats); }); } @@ -204,8 +173,7 @@ API description document (or documents) not found on path: } else { this.readLocalFile(fileUrlOrPath, loadCallback); } - }, - callback); + }, callback); } downloadFile(fileUrl, callback) { @@ -230,7 +198,10 @@ Server did not send any blueprint back and responded with status code ${res.stat `); return callback(err, this.stats); } - this.configuration.data[fileUrl] = { raw: body, filename: fileUrl }; + this.configuration.apiDescriptions.push({ + location: fileUrl, + content: body, + }); callback(null, this.stats); }); } @@ -244,7 +215,10 @@ Is the provided path correct? `); return callback(err); } - this.configuration.data[filePath] = { raw: data, filename: filePath }; + this.configuration.apiDescriptions.push({ + location: filePath, + content: data, + }); callback(null, this.stats); }); } @@ -254,30 +228,31 @@ Is the provided path correct? this.transactions = []; // Compile HTTP transactions for each API description - async.each(Object.keys(this.configuration.data), (filename, next) => { - const fileData = this.configuration.data[filename]; - if (!fileData.annotations) { fileData.annotations = []; } + async.each(this.configuration.apiDescriptions, (apiDescription, next) => { + apiDescription.annotations = []; - this.logger.debug(`Parsing API description: ${filename}`); - parse(fileData.raw, (parseErr, parseResult) => { + this.logger.debug(`Parsing API description: ${apiDescription.location}`); + parse(apiDescription.content, (parseErr, parseResult) => { if (parseErr) { next(parseErr); return; } - this.logger.debug(`Compiling HTTP transactions from API description: ${filename}`); + this.logger.debug(`Compiling HTTP transactions from API description: ${apiDescription.location}`); let compileResult; try { - compileResult = compile(parseResult.mediaType, parseResult.apiElements, filename); + compileResult = compile(parseResult.mediaType, parseResult.apiElements, apiDescription.location); } catch (compileErr) { next(compileErr); return; } - fileData.mediaType = compileResult.mediaType; - fileData.annotations = fileData.annotations.concat(compileResult.annotations); - this.transactions = this.transactions.concat(compileResult.transactions); + apiDescription.mediaType = compileResult.mediaType; + apiDescription.annotations = apiDescription.annotations.concat(compileResult.annotations); + this.transactions = this.transactions + .concat(compileResult.transactions) + .map(transaction => (Object.assign({ apiDescriptionMediaType: compileResult.mediaType }, transaction))); next(); }); }, (runtimeError) => { - if (!runtimeError) { runtimeError = handleRuntimeProblems(this.configuration.data, this.logger); } + if (!runtimeError) { runtimeError = handleRuntimeProblems(this.configuration.apiDescriptions, this.logger); } callback(runtimeError, this.stats); }); } @@ -289,7 +264,7 @@ Is the provided path correct? // When event 'start' is emitted, function in callback is executed for each // reporter registered by listeners - this.configuration.emitter.emit('start', this.configuration.data, (reporterError) => { + this.configuration.emitter.emit('start', this.configuration.apiDescriptions, (reporterError) => { if (reporterError) { this.logger.error(reporterError.message); } // Last called reporter callback function starts the runner diff --git a/lib/TransactionRunner.js b/lib/TransactionRunner.js index eda5306b1..2aa969d44 100644 --- a/lib/TransactionRunner.js +++ b/lib/TransactionRunner.js @@ -38,7 +38,7 @@ class TransactionRunner { config(config) { this.configuration = config; - this.multiBlueprint = Object.keys(this.configuration.data).length > 1; + this.multiBlueprint = this.configuration.apiDescriptions.length > 1; } run(transactions, callback) { @@ -197,13 +197,7 @@ class TransactionRunner { configureTransaction(transaction) { const { configuration } = this; - const { origin, request, response } = transaction; - const mediaType = ( - configuration.data[origin.filename] - ? configuration.data[origin.filename].mediaType - : undefined - ) || 'text/vnd.apiblueprint'; // Parse the server URL (just once, caching it in @parsedUrl) if (!this.parsedUrl) { this.parsedUrl = this.parseServerUrl(configuration.server); } @@ -247,12 +241,13 @@ class TransactionRunner { // Transaction skipping (can be modified in hooks). If the input format // is OpenAPI 2, non-2xx transactions should be skipped by default. let skip = false; - if (mediaType.indexOf('swagger') !== -1) { + if (transaction.apiDescriptionMediaType.includes('swagger')) { const status = parseInt(response.status, 10); if ((status < 200) || (status >= 300)) { skip = true; } } + delete transaction.apiDescriptionMediaType; const configuredTransaction = { name: transaction.name, diff --git a/lib/configuration.js b/lib/configuration.js index 52fc5cac4..3eb0105fd 100644 --- a/lib/configuration.js +++ b/lib/configuration.js @@ -21,6 +21,11 @@ function coerceToBoolean(value) { } +function unique(array) { + return Array.from(new Set(array)); +} + + function applyLoggingOptions(options) { if (options.color === false) { logger.transports.console.colorize = false; @@ -126,6 +131,27 @@ function coerceRemovedOptions(config = {}) { config.options.path = [].concat([config.blueprintPath], coerceToArray(config.options.path)); delete config.blueprintPath; } + if (config.data) { + warnings.push("DEPRECATED: The 'data' configuration property is deprecated " + + "in favor of 'apiDescriptions', please see https://dredd.org"); + + const apiDescriptions = Object.keys(config.data).map((location) => { + if (typeof config.data[location] === 'string') { + return { + location, + content: config.data[location], + }; + } + return { + location: config.data[location].filename, + content: config.data[location].raw, + }; + }); + config.apiDescriptions = config.apiDescriptions + ? config.apiDescriptions.concat(apiDescriptions) + : apiDescriptions; + delete config.data; + } return { errors, warnings }; } @@ -139,6 +165,7 @@ function applyConfiguration(inConfig) { // Keep commented-out, so these values are actually set by CLI // cwd: process.cwd() }, + apiDescriptions: [], options: { 'dry-run': false, reporter: null, @@ -166,6 +193,13 @@ function applyConfiguration(inConfig) { }, }; + // Transform apiDescriptions from strings to an array of objects + outConfig.apiDescriptions = coerceToArray(inConfig.apiDescriptions) + .map((apiDescription, i) => ({ + location: `configuration.apiDescriptions[${i}]`, + content: apiDescription, + })); + // Gracefully deal with the removed options const coerceResult = coerceRemovedOptions(inConfig); @@ -188,7 +222,7 @@ function applyConfiguration(inConfig) { outConfig.options.header = coerceToArray(outConfig.options.header); outConfig.options.method = coerceToArray(outConfig.options.method); outConfig.options.only = coerceToArray(outConfig.options.only); - outConfig.options.path = coerceToArray(outConfig.options.path); + outConfig.options.path = unique(coerceToArray(outConfig.options.path)); outConfig.options.method = outConfig.options.method .map(method => method.toUpperCase()); diff --git a/lib/handleRuntimeProblems.js b/lib/handleRuntimeProblems.js index c7fd1ba5b..6debe9776 100644 --- a/lib/handleRuntimeProblems.js +++ b/lib/handleRuntimeProblems.js @@ -2,16 +2,13 @@ const blueprintUtils = require('./blueprintUtils'); const defaultLogger = require('./logger'); -module.exports = function handleRuntimeProblems(blueprintData, logger) { +module.exports = function handleRuntimeProblems(apiDescriptions, logger) { logger = logger || defaultLogger; let error = false; - for (const filename of Object.keys(blueprintData || {})) { - const data = blueprintData[filename]; - const apiDescriptionDocument = data.raw; - - for (const annotation of data.annotations) { + apiDescriptions.forEach((apiDescription) => { + for (const annotation of apiDescription.annotations) { let log; let message; if (annotation.type === 'warning') { @@ -22,8 +19,8 @@ module.exports = function handleRuntimeProblems(blueprintData, logger) { } if (annotation.component === 'apiDescriptionParser') { - const ranges = blueprintUtils.warningLocationToRanges(annotation.location, apiDescriptionDocument); - message = `Parser ${annotation.type} in file '${filename}': ${annotation.message}`; + const ranges = blueprintUtils.warningLocationToRanges(annotation.location, apiDescription.content); + message = `Parser ${annotation.type} in '${apiDescription.location}': ${annotation.message}`; if (ranges && ranges.length) { message += ` on ${blueprintUtils.rangesToLinesText(ranges)}`; } @@ -35,10 +32,10 @@ module.exports = function handleRuntimeProblems(blueprintData, logger) { annotation.origin.resourceName, annotation.origin.actionName, ].filter(part => !!part).join(' > '); - log(`Compilation ${annotation.type} in file '${filename}': ${annotation.message} (${transactionName})`); + log(`Compilation ${annotation.type} in '${apiDescription.location}': ${annotation.message} (${transactionName})`); } } - } + }); if (error) { return new Error('Error when processing API description.'); diff --git a/lib/reporters/ApiaryReporter.js b/lib/reporters/ApiaryReporter.js index 3803d9a0c..05abcf5c1 100644 --- a/lib/reporters/ApiaryReporter.js +++ b/lib/reporters/ApiaryReporter.js @@ -90,7 +90,7 @@ ApiaryReporter.prototype._getKeys = function _getKeys() { ApiaryReporter.prototype.configureEmitter = function configureEmitter(emitter) { - emitter.on('start', (blueprintsData, callback) => { + emitter.on('start', (apiDescriptions, callback) => { if (this.serverError === true) { return callback(); } this.uuid = generateUuid(); this.startedAt = Math.round(new Date().getTime() / 1000); @@ -108,13 +108,12 @@ ApiaryReporter.prototype.configureEmitter = function configureEmitter(emitter) { } // Transform blueprints data to array - const blueprints = []; - for (const filename of Object.keys(blueprintsData || {})) { - blueprints.push(blueprintsData[filename]); - } - const data = { - blueprints, + blueprints: apiDescriptions.map(apiDescription => ({ + filename: apiDescription.location, + raw: apiDescription.content, + annotations: apiDescription.annotations, + })), endpoint: this.config.server, agent: this._get('dreddAgent', 'DREDD_AGENT') || this._get('user', 'USER'), agentRunUuid: this.uuid, diff --git a/lib/reporters/BaseReporter.js b/lib/reporters/BaseReporter.js index 136c94a23..4ae5f7252 100644 --- a/lib/reporters/BaseReporter.js +++ b/lib/reporters/BaseReporter.js @@ -9,7 +9,7 @@ function BaseReporter(emitter, stats, tests) { } BaseReporter.prototype.configureEmitter = function configureEmitter(emitter) { - emitter.on('start', (rawBlueprint, callback) => { + emitter.on('start', (apiDescriptions, callback) => { this.stats.start = new Date(); callback(); }); diff --git a/lib/reporters/CLIReporter.js b/lib/reporters/CLIReporter.js index 039c7ddd7..f5062079c 100644 --- a/lib/reporters/CLIReporter.js +++ b/lib/reporters/CLIReporter.js @@ -28,7 +28,7 @@ function CLIReporter(emitter, stats, tests, inlineErrors, details) { } CLIReporter.prototype.configureEmitter = function configureEmitter(emitter) { - emitter.on('start', (rawBlueprint, callback) => { + emitter.on('start', (apiDescriptions, callback) => { logger.debug('Beginning Dredd testing...'); callback(); }); diff --git a/lib/reporters/DotReporter.js b/lib/reporters/DotReporter.js index 9949e3561..706f55874 100644 --- a/lib/reporters/DotReporter.js +++ b/lib/reporters/DotReporter.js @@ -14,7 +14,7 @@ function DotReporter(emitter, stats, tests) { } DotReporter.prototype.configureEmitter = function configureEmitter(emitter) { - emitter.on('start', (rawBlueprint, callback) => { + emitter.on('start', (apiDescriptions, callback) => { logger.debug('Beginning Dredd testing...'); callback(); }); diff --git a/lib/reporters/HTMLReporter.js b/lib/reporters/HTMLReporter.js index 5d1cb4313..78b6ab3d0 100644 --- a/lib/reporters/HTMLReporter.js +++ b/lib/reporters/HTMLReporter.js @@ -38,7 +38,7 @@ HTMLReporter.prototype.sanitizedPath = function sanitizedPath(path = './report.h HTMLReporter.prototype.configureEmitter = function configureEmitter(emitter) { const title = str => `${Array(this.level).join('#')} ${str}`; - emitter.on('start', (rawBlueprint, callback) => { + emitter.on('start', (apiDescriptions, callback) => { this.level++; this.buf += `${title('Dredd Tests')}\n`; callback(); diff --git a/lib/reporters/MarkdownReporter.js b/lib/reporters/MarkdownReporter.js index 3b54d43c2..0ffd2c299 100644 --- a/lib/reporters/MarkdownReporter.js +++ b/lib/reporters/MarkdownReporter.js @@ -37,7 +37,7 @@ MarkdownReporter.prototype.sanitizedPath = function sanitizedPath(path = './repo MarkdownReporter.prototype.configureEmitter = function configureEmitter(emitter) { const title = str => `${Array(this.level).join('#')} ${str}`; - emitter.on('start', (rawBlueprint, callback) => { + emitter.on('start', (apiDescriptions, callback) => { this.level++; this.buf += `${title('Dredd Tests')}\n`; callback(); diff --git a/lib/reporters/NyanReporter.js b/lib/reporters/NyanReporter.js index 332e89604..47b760aa2 100644 --- a/lib/reporters/NyanReporter.js +++ b/lib/reporters/NyanReporter.js @@ -38,7 +38,7 @@ function NyanCatReporter(emitter, stats, tests) { } NyanCatReporter.prototype.configureEmitter = function configureEmitter(emitter) { - emitter.on('start', (rawBlueprint, callback) => { + emitter.on('start', (apiDescriptions, callback) => { this.cursorHide(); this.draw(); callback(); diff --git a/lib/reporters/XUnitReporter.js b/lib/reporters/XUnitReporter.js index 5c262af58..a9b768e10 100644 --- a/lib/reporters/XUnitReporter.js +++ b/lib/reporters/XUnitReporter.js @@ -85,7 +85,7 @@ XUnitReporter.prototype.sanitizedPath = function sanitizedPath(path = './report. }; XUnitReporter.prototype.configureEmitter = function configureEmitter(emitter) { - emitter.on('start', (rawBlueprint, callback) => { + emitter.on('start', (apiDescriptions, callback) => { makeDir(pathmodule.dirname(this.path)) .then(() => { this.appendLine(this.path, this.toTag('testsuite', { diff --git a/test/fixtures/blueprint-data.js b/test/fixtures/apiDescriptions.js similarity index 68% rename from test/fixtures/blueprint-data.js rename to test/fixtures/apiDescriptions.js index 713d7d43e..06ba73b63 100644 --- a/test/fixtures/blueprint-data.js +++ b/test/fixtures/apiDescriptions.js @@ -1,7 +1,7 @@ -module.exports = { - './test/fixtures/multiple-examples.apib': { - raw: 'FORMAT: 1A\n\n# Machines API\n\n# Group Machines\n\n# Machines collection [/machines/{id}]\n + Parameters\n - id (number, `1`)\n\n## Get Machines [GET]\n\n- Request (application/json)\n + Parameters\n - id (number, `2`)\n\n- Response 200 (application/json; charset=utf-8)\n\n [\n {\n "type": "bulldozer",\n "name": "willy"\n }\n ]\n\n- Request (application/json)\n + Parameters\n - id (number, `3`)\n\n- Response 200 (application/json; charset=utf-8)\n\n [\n {\n "type": "bulldozer",\n "name": "willy"\n }\n ]\n', - filename: './test/fixtures/multiple-examples.apib', +module.exports = [ + { + content: 'FORMAT: 1A\n\n# Machines API\n\n# Group Machines\n\n# Machines collection [/machines/{id}]\n + Parameters\n - id (number, `1`)\n\n## Get Machines [GET]\n\n- Request (application/json)\n + Parameters\n - id (number, `2`)\n\n- Response 200 (application/json; charset=utf-8)\n\n [\n {\n "type": "bulldozer",\n "name": "willy"\n }\n ]\n\n- Request (application/json)\n + Parameters\n - id (number, `3`)\n\n- Response 200 (application/json; charset=utf-8)\n\n [\n {\n "type": "bulldozer",\n "name": "willy"\n }\n ]\n', + location: './test/fixtures/multiple-examples.apib', annotations: [ { component: 'apiDescriptionParser', @@ -47,4 +47,4 @@ module.exports = { }, ], }, -}; +]; diff --git a/test/integration/annotations-test.js b/test/integration/annotations-test.js index 47d07e0cd..53d8f52e6 100644 --- a/test/integration/annotations-test.js +++ b/test/integration/annotations-test.js @@ -7,7 +7,10 @@ const Dredd = require('../../lib/Dredd'); function compileTransactions(apiDescription, logger, callback) { const dredd = new Dredd({}); dredd.logger = logger; - dredd.configuration.data = { 'filename.api': { raw: apiDescription } }; + dredd.configuration.apiDescriptions = [{ + location: 'filename.api', + content: apiDescription, + }]; dredd.compileTransactions(callback); } @@ -35,7 +38,7 @@ FORMAT: 1A it('logs the warnings with line numbers', () => { assert.match( logger.warn.getCall(0).args[0], - /^parser warning in file 'filename\.api': [\s\S]+ on line 5$/i + /^parser warning in 'filename\.api': [\s\S]+ on line 5$/i ); }); }); @@ -63,7 +66,7 @@ FORMAT: 1A it('logs the errors with line numbers', () => { assert.match( logger.error.getCall(0).args[0], - /^parser error in file 'filename\.api': [\s\S]+ on line 6$/i + /^parser error in 'filename\.api': [\s\S]+ on line 6$/i ); }); }); @@ -90,7 +93,7 @@ FORMAT: 1A it('logs the warnings with a transaction path', () => { assert.match( logger.warn.getCall(0).args[0], - /^compilation warning in file 'filename\.api': [\s\S]+ \(Dummy API > Index > Index\)$/i + /^compilation warning in 'filename\.api': [\s\S]+ \(Dummy API > Index > Index\)$/i ); }); }); @@ -119,7 +122,7 @@ FORMAT: 1A it('logs the errors with a transaction path', () => { assert.match( logger.error.getCall(0).args[0], - /^compilation error in file 'filename\.api': [\s\S]+ \(Dummy API > Index > Index\)$/i + /^compilation error in 'filename\.api': [\s\S]+ \(Dummy API > Index > Index\)$/i ); }); }); diff --git a/test/integration/sanitation-test.js b/test/integration/sanitation-test.js index 3eb89446f..da57335e7 100644 --- a/test/integration/sanitation-test.js +++ b/test/integration/sanitation-test.js @@ -28,7 +28,7 @@ describe('Sanitation of Reported Data', () => { // 'start' and 'end' events are asynchronous and they do not carry any data // significant for following scenarios - emitter.on('start', (apiDescription, cb) => { events.push({ name: 'start' }); return cb(); }); + emitter.on('start', (apiDescriptions, cb) => { events.push({ name: 'start' }); return cb(); }); emitter.on('end', (cb) => { events.push({ name: 'end' }); return cb(); }); return emitter; diff --git a/test/unit/Dredd-test.js b/test/unit/Dredd-test.js index 0538fcb65..bb3656c45 100644 --- a/test/unit/Dredd-test.js +++ b/test/unit/Dredd-test.js @@ -21,6 +21,14 @@ const Dredd = proxyquire('../../lib/Dredd', { './logger': loggerStub, }); + +function compareLocation(ad1, ad2) { + if (ad1.location < ad2.location) { return -1; } + if (ad1.location > ad2.location) { return 1; } + return 0; +} + + describe('Dredd class', () => { let configuration = {}; let dredd = {}; @@ -91,25 +99,28 @@ describe('Dredd class', () => { }); describe('when paths specified with glob paterns', () => { - before(() => { + beforeEach(() => { configuration = { server: 'http://127.0.0.1:3000/', options: { - path: ['./test/fixtures/multifile/*.apib', './test/fixtures/multifile/*.apib'], }, }; dredd = new Dredd(configuration); + sinon + .stub(dredd.runner, 'executeTransaction') + .callsFake((transaction, hooks, callback) => callback()); }); - - beforeEach(() => sinon.stub(dredd.runner, 'executeTransaction').callsFake((transaction, hooks, callback) => callback())); - afterEach(() => dredd.runner.executeTransaction.restore()); it('should expand all glob patterns and resolved paths should be unique', done => dredd.run((error) => { if (error) { return done(error); } - assert.equal(dredd.configuration.files.length, 3); - assert.include(dredd.configuration.files, './test/fixtures/multifile/message.apib'); + assert.lengthOf(dredd.configuration.files, 3); + assert.deepEqual(dredd.configuration.files, [ + './test/fixtures/multifile/greeting.apib', + './test/fixtures/multifile/message.apib', + './test/fixtures/multifile/name.apib', + ]); done(); })); @@ -121,20 +132,31 @@ describe('Dredd class', () => { it('should load file contents on paths to config', done => dredd.run((error) => { if (error) { return done(error); } - assert.isObject(dredd.configuration.data); - assert.property(dredd.configuration.data, './test/fixtures/multifile/greeting.apib'); - assert.isObject(dredd.configuration.data['./test/fixtures/multifile/greeting.apib']); - assert.property(dredd.configuration.data['./test/fixtures/multifile/greeting.apib'], 'filename'); - assert.property(dredd.configuration.data['./test/fixtures/multifile/greeting.apib'], 'raw'); + assert.lengthOf(dredd.configuration.apiDescriptions, 3); + dredd.configuration.apiDescriptions.sort(compareLocation); + + assert.isObject(dredd.configuration.apiDescriptions[0]); + assert.propertyVal(dredd.configuration.apiDescriptions[0], 'location', './test/fixtures/multifile/greeting.apib'); + assert.property(dredd.configuration.apiDescriptions[0], 'content'); + + assert.isObject(dredd.configuration.apiDescriptions[1]); + assert.propertyVal(dredd.configuration.apiDescriptions[1], 'location', './test/fixtures/multifile/message.apib'); + assert.property(dredd.configuration.apiDescriptions[1], 'content'); + + assert.isObject(dredd.configuration.apiDescriptions[2]); + assert.propertyVal(dredd.configuration.apiDescriptions[2], 'location', './test/fixtures/multifile/name.apib'); + assert.property(dredd.configuration.apiDescriptions[2], 'content'); done(); })); it('should parse loaded files', done => dredd.run((error) => { if (error) { return done(error); } - assert.isObject(dredd.configuration.data['./test/fixtures/multifile/greeting.apib']); - assert.property(dredd.configuration.data['./test/fixtures/multifile/greeting.apib'], 'annotations'); - assert.property(dredd.configuration.data['./test/fixtures/multifile/greeting.apib'], 'filename'); - assert.property(dredd.configuration.data['./test/fixtures/multifile/greeting.apib'], 'raw'); + assert.lengthOf(dredd.configuration.apiDescriptions, 3); + dredd.configuration.apiDescriptions.sort(compareLocation); + + assert.property(dredd.configuration.apiDescriptions[0], 'annotations'); + assert.property(dredd.configuration.apiDescriptions[1], 'annotations'); + assert.property(dredd.configuration.apiDescriptions[2], 'annotations'); done(); })); }); @@ -229,19 +251,18 @@ GET /url it('should pass data contents to config', done => dredd.run((error) => { if (error) { return done(error); } - assert.isObject(dredd.configuration.data); - assert.notNestedProperty(dredd, 'configuration.data.testingDirectObject'); - assert.nestedPropertyVal(dredd, 'configuration.data.testingDirectObjectFilename.filename', 'testingDirectObjectFilename'); - assert.nestedProperty(dredd, 'configuration.data.testingDirectObjectFilename.raw'); - assert.nestedPropertyVal(dredd, 'configuration.data.testingDirectBlueprintString.filename', 'testingDirectBlueprintString'); - assert.nestedProperty(dredd, 'configuration.data.testingDirectBlueprintString.raw'); - done(); - })); - - it('should parse passed data contents', done => dredd.run((error) => { - if (error) { return done(error); } - assert.nestedProperty(dredd, 'configuration.data.testingDirectObjectFilename.annotations'); - assert.nestedProperty(dredd, 'configuration.data.testingDirectBlueprintString.annotations'); + assert.lengthOf(dredd.configuration.apiDescriptions, 2); + dredd.configuration.apiDescriptions.sort(compareLocation); + + assert.isObject(dredd.configuration.apiDescriptions[0]); + assert.propertyVal(dredd.configuration.apiDescriptions[0], 'location', 'testingDirectBlueprintString'); + assert.property(dredd.configuration.apiDescriptions[0], 'content'); + assert.property(dredd.configuration.apiDescriptions[0], 'annotations'); + + assert.isObject(dredd.configuration.apiDescriptions[1]); + assert.propertyVal(dredd.configuration.apiDescriptions[1], 'location', 'testingDirectObjectFilename'); + assert.property(dredd.configuration.apiDescriptions[1], 'content'); + assert.property(dredd.configuration.apiDescriptions[1], 'annotations'); done(); })); @@ -259,18 +280,22 @@ GET /url it('should fill configuration data with data and one file at that path', done => localdredd.run((error) => { if (error) { return done(error); } assert.lengthOf(localdredd.configuration.files, 1); - assert.isObject(localdredd.configuration.data); - assert.lengthOf(Object.keys(localdredd.configuration.data), 3); - assert.property(localdredd.configuration.data, './test/fixtures/apiary.apib'); - assert.propertyVal(localdredd.configuration.data['./test/fixtures/apiary.apib'], 'filename', './test/fixtures/apiary.apib'); - assert.property(localdredd.configuration.data['./test/fixtures/apiary.apib'], 'raw'); - assert.property(localdredd.configuration.data['./test/fixtures/apiary.apib'], 'annotations'); - assert.nestedPropertyVal(localdredd, 'configuration.data.testingDirectObjectFilename.filename', 'testingDirectObjectFilename'); - assert.nestedProperty(localdredd, 'configuration.data.testingDirectObjectFilename.raw'); - assert.nestedProperty(localdredd, 'configuration.data.testingDirectObjectFilename.annotations'); - assert.nestedPropertyVal(localdredd, 'configuration.data.testingDirectBlueprintString.filename', 'testingDirectBlueprintString'); - assert.nestedProperty(localdredd, 'configuration.data.testingDirectBlueprintString.raw'); - assert.nestedProperty(localdredd, 'configuration.data.testingDirectBlueprintString.annotations'); + assert.lengthOf(localdredd.configuration.apiDescriptions, 3); + + assert.isObject(localdredd.configuration.apiDescriptions[0]); + assert.propertyVal(localdredd.configuration.apiDescriptions[0], 'location', 'testingDirectObjectFilename'); + assert.property(localdredd.configuration.apiDescriptions[0], 'content'); + assert.property(localdredd.configuration.apiDescriptions[0], 'annotations'); + + assert.isObject(localdredd.configuration.apiDescriptions[1]); + assert.propertyVal(localdredd.configuration.apiDescriptions[1], 'location', 'testingDirectBlueprintString'); + assert.property(localdredd.configuration.apiDescriptions[1], 'content'); + assert.property(localdredd.configuration.apiDescriptions[1], 'annotations'); + + assert.isObject(localdredd.configuration.apiDescriptions[2]); + assert.propertyVal(localdredd.configuration.apiDescriptions[2], 'location', './test/fixtures/apiary.apib'); + assert.property(localdredd.configuration.apiDescriptions[2], 'content'); + assert.property(localdredd.configuration.apiDescriptions[2], 'annotations'); done(); })); }); @@ -279,11 +304,10 @@ GET /url describe('when paths are specified as a mix of URLs and a glob path', () => { let blueprintCode; - before((done) => { + beforeEach((done) => { configuration = { server: 'http://127.0.0.1:3000/', options: { - path: ['http://some.path.to/file.apib', 'https://another.path.to/apiary.apib', './test/fixtures/multifile/*.apib'], }, }; @@ -292,10 +316,11 @@ GET /url blueprintCode = content.toString(); done(err); }); + sinon + .stub(dredd.runner, 'executeTransaction') + .callsFake((transaction, hooks, callback) => callback()); }); - beforeEach(() => sinon.stub(dredd.runner, 'executeTransaction').callsFake((transaction, hooks, callback) => callback())); - afterEach(() => dredd.runner.executeTransaction.restore()); describe('when all URLs can be downloaded', () => { @@ -308,11 +333,11 @@ GET /url it('should expand glob pattern and resolved paths should be unique', done => dredd.run((error) => { if (error) { return done(error); } assert.lengthOf(dredd.configuration.files, 5); - assert.sameMembers(dredd.configuration.files, [ + assert.deepEqual(dredd.configuration.files, [ 'http://some.path.to/file.apib', 'https://another.path.to/apiary.apib', - './test/fixtures/multifile/message.apib', './test/fixtures/multifile/greeting.apib', + './test/fixtures/multifile/message.apib', './test/fixtures/multifile/name.apib', ]); done(); @@ -326,35 +351,35 @@ GET /url it('should load file contents on paths to config and parse these files', done => dredd.run((error) => { if (error) { return done(error); } - assert.isObject(dredd.configuration.data); - assert.property(dredd.configuration.data, './test/fixtures/multifile/greeting.apib'); - assert.property(dredd.configuration.data, 'http://some.path.to/file.apib'); - assert.property(dredd.configuration.data, 'https://another.path.to/apiary.apib'); - - assert.isObject(dredd.configuration.data['./test/fixtures/multifile/name.apib']); - assert.property(dredd.configuration.data['./test/fixtures/multifile/name.apib'], 'filename'); - assert.property(dredd.configuration.data['./test/fixtures/multifile/name.apib'], 'raw'); - assert.property(dredd.configuration.data['./test/fixtures/multifile/name.apib'], 'annotations'); - - assert.isObject(dredd.configuration.data['./test/fixtures/multifile/message.apib']); - assert.property(dredd.configuration.data['./test/fixtures/multifile/message.apib'], 'filename'); - assert.property(dredd.configuration.data['./test/fixtures/multifile/message.apib'], 'raw'); - assert.property(dredd.configuration.data['./test/fixtures/multifile/message.apib'], 'annotations'); - - assert.isObject(dredd.configuration.data['./test/fixtures/multifile/greeting.apib']); - assert.property(dredd.configuration.data['./test/fixtures/multifile/greeting.apib'], 'filename'); - assert.property(dredd.configuration.data['./test/fixtures/multifile/greeting.apib'], 'raw'); - assert.property(dredd.configuration.data['./test/fixtures/multifile/greeting.apib'], 'annotations'); - - assert.isObject(dredd.configuration.data['http://some.path.to/file.apib']); - assert.property(dredd.configuration.data['http://some.path.to/file.apib'], 'filename'); - assert.property(dredd.configuration.data['http://some.path.to/file.apib'], 'raw'); - assert.property(dredd.configuration.data['http://some.path.to/file.apib'], 'annotations'); - - assert.isObject(dredd.configuration.data['https://another.path.to/apiary.apib']); - assert.property(dredd.configuration.data['https://another.path.to/apiary.apib'], 'filename'); - assert.property(dredd.configuration.data['https://another.path.to/apiary.apib'], 'raw'); - assert.property(dredd.configuration.data['https://another.path.to/apiary.apib'], 'annotations'); + + assert.lengthOf(dredd.configuration.apiDescriptions, 5); + dredd.configuration.apiDescriptions.sort(compareLocation); + + assert.isObject(dredd.configuration.apiDescriptions[0]); + assert.propertyVal(dredd.configuration.apiDescriptions[0], 'location', './test/fixtures/multifile/greeting.apib'); + assert.property(dredd.configuration.apiDescriptions[0], 'content'); + assert.property(dredd.configuration.apiDescriptions[0], 'annotations'); + + assert.isObject(dredd.configuration.apiDescriptions[1]); + assert.propertyVal(dredd.configuration.apiDescriptions[1], 'location', './test/fixtures/multifile/message.apib'); + assert.property(dredd.configuration.apiDescriptions[1], 'content'); + assert.property(dredd.configuration.apiDescriptions[1], 'annotations'); + + assert.isObject(dredd.configuration.apiDescriptions[2]); + assert.propertyVal(dredd.configuration.apiDescriptions[2], 'location', './test/fixtures/multifile/name.apib'); + assert.property(dredd.configuration.apiDescriptions[2], 'content'); + assert.property(dredd.configuration.apiDescriptions[2], 'annotations'); + + assert.isObject(dredd.configuration.apiDescriptions[3]); + assert.propertyVal(dredd.configuration.apiDescriptions[3], 'location', 'http://some.path.to/file.apib'); + assert.property(dredd.configuration.apiDescriptions[3], 'content'); + assert.property(dredd.configuration.apiDescriptions[3], 'annotations'); + + assert.isObject(dredd.configuration.apiDescriptions[4]); + assert.propertyVal(dredd.configuration.apiDescriptions[4], 'location', 'https://another.path.to/apiary.apib'); + assert.property(dredd.configuration.apiDescriptions[4], 'content'); + assert.property(dredd.configuration.apiDescriptions[4], 'annotations'); + done(); })); }); diff --git a/test/unit/configuration-test.js b/test/unit/configuration-test.js index a6cd2b586..fa4d234a9 100644 --- a/test/unit/configuration-test.js +++ b/test/unit/configuration-test.js @@ -546,4 +546,99 @@ describe('configuration._coerceRemovedOptions()', () => { assert.lengthOf(coerceResult.warnings, 1); }); }); + + describe('with data set to { filename: apiDescription }', () => { + const config = { data: { 'filename.api': 'FORMAT: 1A\n# Sample API\n' } }; + let coerceResult; + + before(() => { + coerceResult = configuration._coerceRemovedOptions(config); + }); + + it('gets reformatted', () => { + assert.deepEqual(config, { + apiDescriptions: [ + { + location: 'filename.api', + content: 'FORMAT: 1A\n# Sample API\n', + }, + ], + }); + }); + it('produces no errors', () => { + assert.deepEqual(coerceResult.errors, []); + }); + it('produces one warning', () => { + assert.lengthOf(coerceResult.warnings, 1); + }); + }); + + describe('with data set to { filename: { filename, raw: apiDescription } }', () => { + const config = { + data: { + 'filename.api': { + raw: 'FORMAT: 1A\n# Sample API\n', + filename: 'filename.api', + }, + }, + }; + let coerceResult; + + before(() => { + coerceResult = configuration._coerceRemovedOptions(config); + }); + + it('gets reformatted', () => { + assert.deepEqual(config, { + apiDescriptions: [ + { + location: 'filename.api', + content: 'FORMAT: 1A\n# Sample API\n', + }, + ], + }); + }); + it('produces no errors', () => { + assert.deepEqual(coerceResult.errors, []); + }); + it('produces one warning', () => { + assert.lengthOf(coerceResult.warnings, 1); + }); + }); + + describe('with both data and apiDescriptions set', () => { + const config = { + data: { 'filename.api': 'FORMAT: 1A\n# Sample API v1\n' }, + apiDescriptions: [{ + location: 'configuration.apiDescriptions[0]', + content: 'FORMAT: 1A\n# Sample API v2\n', + }], + }; + let coerceResult; + + before(() => { + coerceResult = configuration._coerceRemovedOptions(config); + }); + + it('gets reformatted', () => { + assert.deepEqual(config, { + apiDescriptions: [ + { + location: 'configuration.apiDescriptions[0]', + content: 'FORMAT: 1A\n# Sample API v2\n', + }, + { + location: 'filename.api', + content: 'FORMAT: 1A\n# Sample API v1\n', + }, + ], + }); + }); + it('produces no errors', () => { + assert.deepEqual(coerceResult.errors, []); + }); + it('produces one warning', () => { + assert.lengthOf(coerceResult.warnings, 1); + }); + }); }); diff --git a/test/unit/handleRuntimeProblems-test.js b/test/unit/handleRuntimeProblems-test.js index e4892e4cb..883ee8520 100644 --- a/test/unit/handleRuntimeProblems-test.js +++ b/test/unit/handleRuntimeProblems-test.js @@ -15,9 +15,9 @@ function prepareData(apiDescriptionDocument, filename, done) { if (err) { done(err); return; } const { annotations } = compile(parseResult.mediaType, parseResult.apiElements, filename); - done(null, { - [filename]: { raw: apiDescriptionDocument, filename, annotations }, - }); + done(null, [ + { content: apiDescriptionDocument, location: filename, annotations }, + ]); }); } diff --git a/test/unit/reporters/ApiaryReporter-test.js b/test/unit/reporters/ApiaryReporter-test.js index 78a10eec3..14a20f8de 100644 --- a/test/unit/reporters/ApiaryReporter-test.js +++ b/test/unit/reporters/ApiaryReporter-test.js @@ -5,7 +5,7 @@ const sinon = require('sinon'); const { assert } = require('chai'); const { EventEmitter } = require('events'); -const blueprintData = require('../../fixtures/blueprint-data'); +const apiDescriptions = require('../../fixtures/apiDescriptions'); const loggerStub = require('../../../lib/logger'); const reporterOutputLoggerStub = require('../../../lib/reporters/reporterOutputLogger'); @@ -281,7 +281,7 @@ describe('ApiaryReporter', () => { it('should set uuid', (done) => { emitter = new EventEmitter(); const apiaryReporter = new ApiaryReporter(emitter, {}, {}, { custom: { apiaryReporterEnv: env } }); - return emitter.emit('start', blueprintData, () => { + return emitter.emit('start', apiDescriptions, () => { assert.isNotNull(apiaryReporter.uuid); return done(); }); @@ -290,7 +290,7 @@ describe('ApiaryReporter', () => { it('should set start time', (done) => { emitter = new EventEmitter(); const apiaryReporter = new ApiaryReporter(emitter, {}, {}, { custom: { apiaryReporterEnv: env } }); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { assert.isNotNull(apiaryReporter.startedAt); done(); }); @@ -299,7 +299,7 @@ describe('ApiaryReporter', () => { it('should call "create new test run" HTTP resource', (done) => { emitter = new EventEmitter(); (new ApiaryReporter(emitter, {}, {}, { custom: { apiaryReporterEnv: env } })); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { assert.isTrue(call.isDone()); done(); }); @@ -308,7 +308,7 @@ describe('ApiaryReporter', () => { it('should attach test run ID back to the reporter as remoteId', (done) => { emitter = new EventEmitter(); const apiaryReporter = new ApiaryReporter(emitter, {}, {}, { custom: { apiaryReporterEnv: env } }); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { assert.isNotNull(apiaryReporter.remoteId); done(); }); @@ -317,7 +317,7 @@ describe('ApiaryReporter', () => { it('should attach test run reportUrl to the reporter as reportUrl', (done) => { emitter = new EventEmitter(); const apiaryReporter = new ApiaryReporter(emitter, {}, {}, { custom: { apiaryReporterEnv: env } }); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { assert.isNotNull(apiaryReporter.reportUrl); done(); }); @@ -326,7 +326,7 @@ describe('ApiaryReporter', () => { it('should have blueprints key in the request and it should be an array and members should have proper structure', (done) => { emitter = new EventEmitter(); (new ApiaryReporter(emitter, {}, {}, { custom: { apiaryReporterEnv: env } })); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { const parsedBody = JSON.parse(requestBody); assert.isArray(parsedBody.blueprints); assert.lengthOf(parsedBody.blueprints, 1); @@ -345,7 +345,7 @@ describe('ApiaryReporter', () => { it('should have various needed keys in test-run payload sent to apiary', (done) => { emitter = new EventEmitter(); (new ApiaryReporter(emitter, {}, {}, { server: 'http://my.server.co:8080', custom: { apiaryReporterEnv: env } })); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { const parsedBody = JSON.parse(requestBody); assert.propertyVal(parsedBody, 'endpoint', 'http://my.server.co:8080'); done(); @@ -355,7 +355,7 @@ describe('ApiaryReporter', () => { it('should send the test-run as public one', (done) => { emitter = new EventEmitter(); (new ApiaryReporter(emitter, {}, {}, { server: 'http://my.server.co:8080', custom: { apiaryReporterEnv: env } })); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { const parsedBody = JSON.parse(requestBody); assert.strictEqual(parsedBody.public, true); done(); @@ -366,7 +366,7 @@ describe('ApiaryReporter', () => { emitter = new EventEmitter(); const apiaryReporter = new ApiaryReporter(emitter, {}, {}, { custom: { apiaryReporterEnv: env } }); apiaryReporter.serverError = true; - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { assert.isFalse(call.isDone()); done(); }); @@ -867,7 +867,7 @@ describe('ApiaryReporter', () => { it('should set uuid', (done) => { emitter = new EventEmitter(); const apiaryReporter = new ApiaryReporter(emitter, {}, {}, { custom: { apiaryReporterEnv: env } }); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { assert.isNotNull(apiaryReporter.uuid); done(); }); @@ -876,7 +876,7 @@ describe('ApiaryReporter', () => { it('should set start time', (done) => { emitter = new EventEmitter(); const apiaryReporter = new ApiaryReporter(emitter, {}, {}, { custom: { apiaryReporterEnv: env } }); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { assert.isNotNull(apiaryReporter.startedAt); done(); }); @@ -885,7 +885,7 @@ describe('ApiaryReporter', () => { it('should call "create new test run" HTTP resource', (done) => { emitter = new EventEmitter(); (new ApiaryReporter(emitter, {}, {}, { custom: { apiaryReporterEnv: env } })); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { assert.isTrue(call.isDone()); done(); }); @@ -894,7 +894,7 @@ describe('ApiaryReporter', () => { it('should attach test run ID back to the reporter as remoteId', (done) => { emitter = new EventEmitter(); const apiaryReporter = new ApiaryReporter(emitter, {}, {}, { custom: { apiaryReporterEnv: env } }); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { assert.isNotNull(apiaryReporter.remoteId); done(); }); @@ -903,7 +903,7 @@ describe('ApiaryReporter', () => { it('should attach test run reportUrl to the reporter as reportUrl', (done) => { emitter = new EventEmitter(); const apiaryReporter = new ApiaryReporter(emitter, {}, {}, { custom: { apiaryReporterEnv: env } }); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { assert.isNotNull(apiaryReporter.reportUrl); done(); }); @@ -912,7 +912,7 @@ describe('ApiaryReporter', () => { it('should send the test-run as non-public', (done) => { emitter = new EventEmitter(); (new ApiaryReporter(emitter, {}, {}, { server: 'http://my.server.co:8080', custom: { apiaryReporterEnv: env } })); - emitter.emit('start', blueprintData, () => { + emitter.emit('start', apiDescriptions, () => { const parsedBody = JSON.parse(requestBody); assert.strictEqual(parsedBody.public, false); done(); diff --git a/test/unit/transactionRunner-test.js b/test/unit/transactionRunner-test.js index 02f975198..1e6af6935 100644 --- a/test/unit/transactionRunner-test.js +++ b/test/unit/transactionRunner-test.js @@ -58,11 +58,11 @@ describe('TransactionRunner', () => { }); describe('config(config)', () => { - describe('when single file in data is present', () => it('should set multiBlueprint to false', () => { + describe('when single file in apiDescriptions is present', () => it('should set multiBlueprint to false', () => { configuration = { server: 'http://127.0.0.1:3000', emitter: new EventEmitter(), - data: { file1: { raw: 'blueprint1' } }, + apiDescriptions: [{ location: 'filename.api', content: '...' }], options: { 'dry-run': false, method: [], @@ -78,11 +78,14 @@ describe('TransactionRunner', () => { assert.notOk(runner.multiBlueprint); })); - describe('when multiple files in data are present', () => it('should set multiBlueprint to true', () => { + describe('when multiple files in apiDescriptions are present', () => it('should set multiBlueprint to true', () => { configuration = { server: 'http://127.0.0.1:3000', emitter: new EventEmitter(), - data: { file1: { raw: 'blueprint1' }, file2: { raw: 'blueprint2' } }, + apiDescriptions: [ + { location: 'filename1.api', content: '...' }, + { location: 'filename2.api', content: '...' }, + ], options: { 'dry-run': false, method: [], @@ -128,6 +131,7 @@ describe('TransactionRunner', () => { actionName: 'Delete Message', exampleName: 'Bogus example name', }, + apiDescriptionMediaType: 'text/vnd.apiblueprint', }; runner = new Runner(configuration); @@ -231,14 +235,10 @@ describe('TransactionRunner', () => { ].forEach(({ description, input, expected }) => context(`${description}: '${input.serverUrl}' + '${input.requestPath}'`, () => { beforeEach(() => { + runner.configuration.server = input.serverUrl; transaction.request.uri = input.requestPath; transaction.origin.filename = filename; - - runner.configuration.server = input.serverUrl; - if (!runner.configuration.data) { runner.configuration.data = {}; } - if (!runner.configuration.data[filename]) { runner.configuration.data[filename] = {}; } - runner.configuration.data[filename].mediaType = 'text/vnd.apiblueprint'; - + transaction.apiDescriptionMediaType = 'text/vnd.apiblueprint'; configuredTransaction = runner.configureTransaction(transaction); }); @@ -259,11 +259,7 @@ describe('TransactionRunner', () => { beforeEach(() => { transaction.response.status = status; transaction.origin.filename = filename; - - if (!runner.configuration.data) { runner.configuration.data = {}; } - if (!runner.configuration.data[filename]) { runner.configuration.data[filename] = {}; } - runner.configuration.data[filename].mediaType = 'application/swagger+json'; - + transaction.apiDescriptionMediaType = 'application/swagger+json'; configuredTransaction = runner.configureTransaction(transaction); }); @@ -279,11 +275,7 @@ describe('TransactionRunner', () => { beforeEach(() => { transaction.response.status = status; transaction.origin.filename = filename; - - if (!runner.configuration.data) { runner.configuration.data = {}; } - if (!runner.configuration.data[filename]) { runner.configuration.data[filename] = {}; } - runner.configuration.data[filename].mediaType = 'application/swagger+json'; - + transaction.apiDescriptionMediaType = 'application/swagger+json'; configuredTransaction = runner.configureTransaction(transaction); }); @@ -298,11 +290,7 @@ describe('TransactionRunner', () => { beforeEach(() => { transaction.response.status = 400; transaction.origin.filename = filename; - - if (!runner.configuration.data) { runner.configuration.data = {}; } - if (!runner.configuration.data[filename]) { runner.configuration.data[filename] = {}; } - runner.configuration.data[filename].mediaType = 'text/plain'; - + transaction.apiDescriptionMediaType = 'text/plain'; configuredTransaction = runner.configureTransaction(transaction); });