From 6deaf4c8537fb7256ce6dcfbe675a7b0fd363995 Mon Sep 17 00:00:00 2001 From: Honza Javorek Date: Mon, 16 May 2016 16:56:47 +0200 Subject: [PATCH] Preventing NaN in printed annotations. --- src/blueprint-utils.coffee | 33 ++-- src/handle-runtime-problems.coffee | 1 - test/unit/blueprint-utils-test.coffee | 17 +- test/unit/dredd-test.coffee | 22 +-- test/unit/handle-runtime-problems-test.coffee | 186 ++++++++++++++++++ 5 files changed, 224 insertions(+), 35 deletions(-) create mode 100644 test/unit/handle-runtime-problems-test.coffee diff --git a/src/blueprint-utils.coffee b/src/blueprint-utils.coffee index 1b6b8ef55..73397cd2a 100644 --- a/src/blueprint-utils.coffee +++ b/src/blueprint-utils.coffee @@ -1,25 +1,25 @@ -newlineRegExp = /\n/g -blueprintUtils = {} +NEWLINE_RE = /\n/g -blueprintUtils.characterIndexToPosition = (charIndex = 0, text = '') -> - pieceOfCode = text.substring 0, charIndex - return { - row: pieceOfCode.match(newlineRegExp)?.length + 1 - } +characterIndexToPosition = (charIndex = 0, code = '') -> + codeFragment = code.substring(0, charIndex) + row = (codeFragment.match(NEWLINE_RE)?.length or 0) + 1 + {row} -blueprintUtils.sortNumbersAscending = (a, b) -> + +sortNumbersAscending = (a, b) -> return a - b -blueprintUtils.warningLocationToRanges = (warningLocation = [], text = '') -> + +warningLocationToRanges = (warningLocation = [], text = '') -> unless warningLocation.length # no start-end ranges, nothing to return return [] rowsIndexes = [] - position = blueprintUtils.characterIndexToPosition(warningLocation[0][0], text) + position = characterIndexToPosition(warningLocation[0][0], text) # add this warning position row into ranges array rowsIndexes.push position.row @@ -29,10 +29,10 @@ blueprintUtils.warningLocationToRanges = (warningLocation = [], text = '') -> if warningLocation.length > 0 # more lines for loc, locKey in warningLocation when locKey > 0 - position = blueprintUtils.characterIndexToPosition(loc[0], text) + position = characterIndexToPosition(loc[0], text) rowsIndexes.push position.row - rowsIndexes.sort(blueprintUtils.sortNumbersAscending) + rowsIndexes.sort(sortNumbersAscending) ranges = [] range = {start: rowsIndexes[0], end: rowsIndexes[0]} for rowIndex in rowsIndexes @@ -46,7 +46,7 @@ blueprintUtils.warningLocationToRanges = (warningLocation = [], text = '') -> return ranges -blueprintUtils.rangesToLinesText = (ranges) -> +rangesToLinesText = (ranges) -> pos = '' for range, rangeIndex in ranges or [] if rangeIndex > 0 @@ -58,4 +58,9 @@ blueprintUtils.rangesToLinesText = (ranges) -> return pos -module.exports = blueprintUtils +module.exports = { + characterIndexToPosition + sortNumbersAscending + warningLocationToRanges + rangesToLinesText +} diff --git a/src/handle-runtime-problems.coffee b/src/handle-runtime-problems.coffee index bb1e5c9ca..9a6b7baf0 100644 --- a/src/handle-runtime-problems.coffee +++ b/src/handle-runtime-problems.coffee @@ -7,7 +7,6 @@ handleRuntimeProblems = (blueprintData) -> error = false for own filename, data of blueprintData - filename = data.filename apiDescriptionDocument = data.raw for annotation in data.annotations diff --git a/test/unit/blueprint-utils-test.coffee b/test/unit/blueprint-utils-test.coffee index aea6ea85e..f9ce78dfe 100644 --- a/test/unit/blueprint-utils-test.coffee +++ b/test/unit/blueprint-utils-test.coffee @@ -9,12 +9,17 @@ describe 'blueprintUtils', -> options = {type: 'refract'} describe 'characterIndexToPosition()', -> - str = null - - it 'returns an object with non-zero-based row', -> - str = "first\nsecond\nthird lines\ncontent continues" - position = blueprintUtils.characterIndexToPosition str.indexOf('lines', str), str - assert.deepEqual position, {row: 3} + describe 'under standard circumstances', -> + it 'returns an object with non-zero-based row', -> + str = "first\nsecond\nthird lines\ncontent continues" + position = blueprintUtils.characterIndexToPosition str.indexOf('lines', str), str + assert.deepEqual position, {row: 3} + + describe 'when given one-line input and zero index', -> + it 'returns an object with row 1', -> + str = "hello\n" + position = blueprintUtils.characterIndexToPosition str.indexOf('hello', str), str + assert.deepEqual position, {row: 1} describe 'warningLocationToRanges()', -> str = null diff --git a/test/unit/dredd-test.coffee b/test/unit/dredd-test.coffee index 91fa03187..30a708695 100644 --- a/test/unit/dredd-test.coffee +++ b/test/unit/dredd-test.coffee @@ -407,10 +407,8 @@ describe 'Dredd class', () -> assert.notOk dredd.runner.executeTransaction.called done() - - describe 'when Blueprint parsing error', () -> - - before () -> + describe 'when Blueprint parsing error', -> + before -> configuration = url: 'http://localhost:3000/' options: @@ -435,9 +433,8 @@ describe 'Dredd class', () -> assert.notOk dredd.runner.executeTransaction.called done() - describe 'when Blueprint parsing warning', () -> - - before () -> + describe 'when Blueprint parsing warning', -> + before -> configuration = url: 'http://localhost:3000/' options: @@ -464,10 +461,8 @@ describe 'Dredd class', () -> assert.ok loggerStub.warn.called done() - - describe 'when non existing Blueprint path', () -> - - beforeEach () -> + describe 'when non existing Blueprint path', -> + beforeEach -> configuration = url: 'http://localhost:3000/' options: @@ -515,9 +510,8 @@ describe 'Dredd class', () -> assert.ok error done() - describe 'when runtime contains any warning', () -> - - beforeEach () -> + describe 'when runtime contains any warning', -> + beforeEach -> configuration = server: 'http://localhost:3000/' options: diff --git a/test/unit/handle-runtime-problems-test.coffee b/test/unit/handle-runtime-problems-test.coffee new file mode 100644 index 000000000..174694c4e --- /dev/null +++ b/test/unit/handle-runtime-problems-test.coffee @@ -0,0 +1,186 @@ + +{assert} = require('chai') +sinon = require('sinon') +proxyquire = require('proxyquire') +dreddTransactions = require('dredd-transactions') + +logger = require('../../src/logger') +handleRuntimeProblems = proxyquire('../../src/handle-runtime-problems', + './logger': logger +) + + +prepareData = (apiDescriptionDocument, filename, done) -> + dreddTransactions.compile(apiDescriptionDocument, filename, (err, {errors, warnings}) -> + return done(err) if err + + annotations = [] + for error in errors + error.type = 'error' + annotations.push(error) + for warning in warnings + warning.type = 'warning' + annotations.push(warning) + + data = {} + data[filename] = {raw: apiDescriptionDocument, filename, annotations} + + done(null, data) + ) + + +describe('handleRuntimeProblems()', -> + warnOutput = undefined + errorOutput = undefined + + beforeEach( -> + warnOutput = '' + errorOutput = '' + + sinon.stub(logger, 'warn', (args...) -> + warnOutput += args.join(' ').toLowerCase() + ) + sinon.stub(logger, 'error', (args...) -> + errorOutput += args.join(' ').toLowerCase() + ) + ) + afterEach( -> + logger.warn.restore() + logger.error.restore() + ) + + describe('Prints parser error', -> + error = undefined + + apiDescriptionDocument = ''' + FORMAT: 1A + # Beehive API + \t\t + ''' + filename = 'dummy-filename.apib' + + beforeEach((done) -> + prepareData(apiDescriptionDocument, filename, (err, data) -> + error = handleRuntimeProblems(data) + done() + ) + ) + + it('returns error', -> + assert.ok(error) + ) + it('has no warning output', -> + assert.equal(warnOutput, '') + ) + it('has error output', -> + assert.ok(errorOutput) + ) + context('the error output', -> + it('mentions it is from parser', -> + assert.include(errorOutput, 'parser') + ) + it('mentions it is error', -> + assert.include(errorOutput, 'error') + ) + it('mentions the filename', -> + assert.include(errorOutput, filename) + ) + it('mentions the line', -> + assert.include(errorOutput, 'on line 3') + ) + it('does not contain any NaNs', -> + assert.notInclude(errorOutput, 'nan') + ) + ) + ) + + describe('Prints parser warning', -> + error = undefined + + apiDescriptionDocument = ''' + FORMAT: 1A + # Beehive API + ## Honey [/honey] + ### Remove [DELETE] + + Response + ''' + filename = 'dummy-filename.apib' + + beforeEach((done) -> + prepareData(apiDescriptionDocument, filename, (err, data) -> + error = handleRuntimeProblems(data) + done() + ) + ) + + it('returns no error', -> + assert.notOk(error) + ) + it('has no error output', -> + assert.equal(errorOutput, '') + ) + it('has warning output', -> + assert.ok(warnOutput) + ) + context('the warning output', -> + it('mentions it is from parser', -> + assert.include(warnOutput, 'parser') + ) + it('mentions it is warning', -> + assert.include(warnOutput, 'warn') + ) + it('mentions the filename', -> + assert.include(warnOutput, filename) + ) + it('mentions the line', -> + assert.include(warnOutput, 'on line 5') + ) + it('does not contain any NaNs', -> + assert.notInclude(warnOutput, 'nan') + ) + ) + ) + + describe('Prints warning about missing title', -> + error = undefined + + apiDescriptionDocument = ''' + So Long, and Thanks for All the Fish! + ''' + filename = 'dummy-filename.apib' + + beforeEach((done) -> + prepareData(apiDescriptionDocument, filename, (err, data) -> + error = handleRuntimeProblems(data) + done() + ) + ) + + it('returns no error', -> + assert.notOk(error) + ) + it('has no error output', -> + assert.equal(errorOutput, '') + ) + it('has warning output', -> + assert.ok(warnOutput) + ) + context('the warning output', -> + it('mentions it is from parser', -> + assert.include(warnOutput, 'parser') + ) + it('mentions it is warning', -> + assert.include(warnOutput, 'warning') + ) + it('mentions the filename', -> + assert.include(warnOutput, filename) + ) + it('mentions the line', -> + assert.include(warnOutput, 'on line 1') + ) + it('does not contain any NaNs', -> + assert.notInclude(warnOutput, 'nan') + ) + ) + ) +)