From 5dc654ad6d9cd14034b17eadd2752eb3a9f4fed9 Mon Sep 17 00:00:00 2001 From: rolux Date: Tue, 29 May 2012 16:18:05 +0200 Subject: [PATCH] Ox.doc: change test object from {statement, result} to {statement, expected}, plus some refactoring; Ox.test: allow source, file, files, docObject or docObjects as first argument, make it testable and add a test, plus some refactoring; Ox.tokenize: add error type, continue lexing on error --- source/Ox/js/JavaScript.js | 240 +++++++++++++++++++++++-------------- 1 file changed, 150 insertions(+), 90 deletions(-) diff --git a/source/Ox/js/JavaScript.js b/source/Ox/js/JavaScript.js index 65adbf51..20717c3f 100644 --- a/source/Ox/js/JavaScript.js +++ b/source/Ox/js/JavaScript.js @@ -2,7 +2,7 @@ /*@ Ox.doc Generates documentation for annotated JavaScript - (source) -> <[o]> Array of doc objects + (source[, callback]) -> <[o]> Array of doc objects (file, callback) -> undefined (files, callback) -> undefined source JavaScript source code @@ -35,6 +35,8 @@ Ox.doc Generates documentation for annotated JavaScript Present if the type of the item is "function". tests <[o]> Tests (array of test objects) + expected Expected result + statement Statement type Type of the item > Ox.test.doc[0].name @@ -64,6 +70,8 @@ Ox.doc Generates documentation for annotated JavaScript ['number'] > Ox.test.doc[1].usage[0].summary 'Bar per baz, or NaN' + > Ox.test.doc[1].tests[1] + {expected: 'NaN', statement: 'My.foo({})'} @*/ Ox.doc = (function() { var re = { @@ -127,12 +135,12 @@ Ox.doc = (function() { item.usage.push(parseNode(node)); } else if (subitem.types[0] == 'event') { item.events = item.events || []; - item.events.push(parseNode(node)); + item.events.push(parseNode(node)); } else { key = item.types[0] == 'function' ? 'arguments' : 'properties' item[key] = item[key] || []; - item[key].push(parseNode(node)); + item[key].push(parseNode(node)); } } else { item.description = item.description @@ -203,14 +211,14 @@ Ox.doc = (function() { section = tree.line.split(' ')[0] } }); - return items; + return items; } function parseTest(string) { // fixme: we cannot properly handle tests where a string contains '\n ' var lines = decodeLinebreaks(string).split('\n '); return { statement: lines[0].slice(2), - result: lines[1].trim() + expected: lines[1].trim() }; } function parseTokens(tokens, includeLeadingLinebreaks) { @@ -311,18 +319,22 @@ Ox.doc = (function() { } return ret; } - return function(/* source | file, callback | files, callback*/) { - var source = arguments.length == 1 ? arguments[0] : void 0, - files = arguments.length == 2 ? Ox.makeArray(arguments[0]) : void 0, - callback = arguments[1], - counter = 0, items = []; - files && files.forEach(function(file) { - Ox.get(file, function(source) { - items = items.concat(parseSource(source, file.split('?')[0])); - ++counter == files.length && callback(items); - }); - }); - return source ? parseSource(source) : void 0; + return function(argument, callback) { + var counter = 0, items = [], ret; + if (arguments.length == 1) { + ret = parseSource(argument); + } else { + argument = Ox.makeArray(argument); + argument.forEach(function(file) { + Ox.get(file, function(source) { + items = items.concat( + parseSource(source, file.split('?')[0]) + ); + ++counter == argument.length && callback(items); + }); + }) + } + return ret; } }()); @@ -567,91 +579,128 @@ Ox.minify = function() { /*@ Ox.test Takes JavaScript, runs inline tests, returns results + (source, callback) -> <[o]> Array of results (file, callback) -> undefined - (files, callback) -> undefines - file Path to JavaScript file - files <[s]> List of paths to J + (files, callback) -> undefined + (doc, callback) -> undefined + (docs, callback) -> undefined + source JavaScript source + file JavaScript file + files <[s]> Array of JavaScript files + doc Documentation object (as returned by Ox.doc) + docs <[o]> Array of documentation objects (as returned by Ox.doc) callback Callback function - results [] Array of results + results <[o]> Array of results actual Actual result expected Expected result name Item name - section Section in the file + section Section in the file statement Test statement passed True if actual result and expected result are equal + + > Ox.test(Ox.test.source, function(r) { Ox.test(r[0].passed, true); }) + undefined @*/ -Ox.test = function(file, callback) { - var regexp = /(Ox\.test\()/; - if (arguments.length == 2) { - Ox.doc(file, function(items) { - var results = []; - file = file.split('?')[0]; - Ox.test.data[file] = { - callback: callback, - done: false, - results: [], - tests: {} - }; - items.forEach(function(item) { - item.tests && item.tests.some(function(test) { - return test.result; - }) && item.tests.forEach(function(test) { - var actual, isAsync = regexp.test(test.statement); - if (isAsync) { - Ox.test.data[file].tests[item.name] = { - section: item.section, - statement: test.statement - }; - test.statement = test.statement.replace( - regexp, '$1\'' + item.name + '\', ' - ); - } - if (test.result || test.statement.match(/Ox\.test/)) { - // don't eval script tags without assignment to Ox.test.foo - actual = eval(test.statement); - Ox.Log('TEST', test.statement); - } - if (!isAsync && test.result) { - Ox.test.data[file].results.push({ - actual: JSON.stringify(actual), - expected: test.result, - name: item.name, - section: item.section, - statement: test.statement, - passed: Ox.isEqual(eval( - '(' + test.result + ')' - ), actual) - }); - } - }); +Ox.test = function(argument, callback) { + Ox.print('Ox.test', argument, '----------------------------------') + function runTests(items) { + var id = Ox.uid(), regexp = /(.+Ox\.test\()/, results = []; + Ox.test.data[id] = { + callback: callback, + done: false, + results: results, + tests: {} + }; + items.forEach(function(item) { + item.tests && item.tests.some(function(test) { + return test.expected; + }) && item.tests.forEach(function(test) { + var actual, isAsync = regexp.test(test.statement); + if (isAsync) { + Ox.test.data[id].tests[test.statement] = { + name: item.name, + section: item.section + }; + Ox.Log('TEST', 'XXX', test.statement); + test.statement = test.statement.replace( + regexp, + "$1'" + test.statement.replace(/'/g, "\\'") + "', " + ); + } + if (test.expected || test.statement.match(/Ox\.test\./)) { + // don't eval script tags without assignment to Ox.test.foo + Ox.Log('TEST', test.statement); + actual = eval(test.statement); + } + if (!isAsync && test.expected) { + Ox.test.data[id].results.push({ + actual: JSON.stringify(actual), + expected: test.expected, + name: item.name, + section: item.section, + statement: test.statement, + passed: Ox.isEqual( + actual, eval('(' + test.expected + ')') + ) + }); + } }); - Ox.test.data[file].done = true; - if (Ox.isEmpty(Ox.test.data[file].tests)) { - callback(Ox.test.data[file].results); - } }); + Ox.test.data[id].done = true; + if (Ox.isEmpty(Ox.test.data[id].tests)) { + callback(Ox.test.data[id].results); + } + } + if (arguments.length == 2) { + if (Ox.typeOf(argument) == 'string' && Ox.contains(argument, '\n')) { + runTests(Ox.doc(argument)) + } else { + argument = Ox.makeArray(argument); + if (Ox.typeOf(argument[0]) == 'string') { + Ox.doc(argument, runTests); + } else { + runTests(argument); + } + } } else { - var name = arguments[0], + var statement = arguments[0], result = arguments[1], - expected = arguments[2]; - file = null; + expected = arguments[2], + id, test; Ox.forEach(Ox.test.data, function(v, k) { - if (v.tests[name]) { - file = k; + if (v.tests[statement]) { + id = k; + test = v.tests[statement]; Ox.Break(); } }); - Ox.test.data[file].results.push({ + Ox.test.data[id].results.push(Ox.extend(test, { actual: result, expected: expected, - name: name, - section: Ox.test.data[file].tests[name].section, - statement: Ox.test.data[file].tests[name].statement, + statement: statement, passed: Ox.isEqual(result, expected) - }); - delete Ox.test.data[file].tests[name]; - if (Ox.test.data[file].done && Ox.isEmpty(Ox.test.data[file].tests)) { - Ox.test.data[file].callback(Ox.test.data[file].results); + })); + delete Ox.test.data[id].tests[statement]; + if (Ox.test.data[id].done && Ox.isEmpty(Ox.test.data[id].tests)) { + Ox.test.data[id].callback(Ox.test.data[id].results); } } }; @@ -663,10 +712,11 @@ Ox.tokenize Tokenizes JavaScript column Column of the token line Line of the token type Type of the token - Type can be "comment", "identifier", - "linebreak", "number", - "operator", "regexp", - "string" or "whitespace" + Type can be "comment", "error", + "identifier", "linebreak", + "number", "operator", + "regexp", "string" or + "whitespace" value Value of the token source JavaScript source code > Ox.tokenize('// comment\nvar foo = bar / baz;').length @@ -790,10 +840,20 @@ Ox.tokenize = (function() { type = 'whitespace'; while (whitespace.indexOf(source[++cursor]) > -1) {} } else { - break; + type = 'error'; + ++cursor; } value = source.slice(start, cursor); - tokens.push({column: column, line: line, type: type, value: value}); + if ( + type == 'error' && tokens.length + && tokens[tokens.length - 1].type == 'error' + ) { + tokens[tokens.length - 1].value += value; + } else { + tokens.push( + {column: column, line: line, type: type, value: value} + ); + } if (type == 'comment') { lines = value.split('\n'); column = lines[lines.length - 1].length;