Commit f88149e5 authored by fxa's avatar fxa

bugfixes according double encoding (Thanks to [RubenVerborgh]) and prefix modifiers

parent dd7a3854
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
UNIT_TESTS = new jake.FileList('test/unit/test*.js').toArray(), UNIT_TESTS = new jake.FileList('test/unit/test*.js').toArray(),
INTEGRATION_TESTS = [ INTEGRATION_TESTS = [
path.join('test', 'integration', 'simpleTest.js'), path.join('test', 'integration', 'simpleTest.js'),
path.join('test', 'integration', 'testExport.js'),
path.join('test', 'integration', 'testRfcSamples.js') path.join('test', 'integration', 'testRfcSamples.js')
], ],
...@@ -59,32 +60,36 @@ ...@@ -59,32 +60,36 @@
ASYNC = {async: true}; ASYNC = {async: true};
var TARGET_UNCOMPRESSED_DEPENDENCIES = (function () {
var all = new jake.FileList(); var all = new jake.FileList();
all.include('./Jakefile.js', 'own-testcases.json'); all.include('./Jakefile.js', 'own-testcases.json');
all.include('src/**'); all.include('src/**');
all.include('test/**'); all.include('test/**');
all.exclude(TARGET_COMPRESSED); all.exclude(TARGET_COMPRESSED);
var TARGET_UNCOMPRESSED_DEPENDENCIES = all.toArray(); return all.toArray();
}());
function closeTask(err) { function closeTask (err) {
if (err) { if (err) {
fail(err); fail(JSON.stringify(err, null, 4));
} }
else {
complete(); complete();
} }
}
desc('clean'); desc('removes all artifacts');
task('clean', [], function () { task('clean', [], function () {
function unlinkWhenExists(filename, callback) { function unlinkWhenExists (filename, callback) {
fs.unlink(filename, function (err) { fs.unlink(filename, function (err) {
callback(err && err.code !== 'ENOENT' ? err : undefined); callback(err && err.code !== 'ENOENT' ? err : undefined);
}); });
} }
async.forEach([TMP_UNTESTED_UNCOMPRESSED, TMP_UNTESTED_COMPRESSED, TARGET_UNCOMPRESSED, TARGET_COMPRESSED], unlinkWhenExists, closeTask); async.forEach([TMP_UNTESTED_UNCOMPRESSED, TMP_UNTESTED_COMPRESSED, TARGET_UNCOMPRESSED, TARGET_COMPRESSED], unlinkWhenExists, closeTask);
}, ASYNC); }, ASYNC);
file(TARGET_UNCOMPRESSED, TARGET_UNCOMPRESSED_DEPENDENCIES, function () { file(TARGET_UNCOMPRESSED, TARGET_UNCOMPRESSED_DEPENDENCIES, function () {
// there is no other way to pass parameters to a testcase -- sorry
global.URI_TEMPLATE_FILE = TMP_UNTESTED_UNCOMPRESSED; global.URI_TEMPLATE_FILE = TMP_UNTESTED_UNCOMPRESSED;
async.series([ async.series([
function (callback) { function (callback) {
...@@ -93,7 +98,7 @@ ...@@ -93,7 +98,7 @@
}, },
function (callback) { function (callback) {
jake.logger.log('unit testing ...'); jake.logger.log('unit testing ...');
nodeunit.reporters[NODEUNIT_REPORTER].run(UNIT_TESTS, NODEUNIT_OPTIONS, callback); nodeunit.reporters['minimal'].run(UNIT_TESTS, NODEUNIT_OPTIONS, callback);
}, },
function (callback) { function (callback) {
jake.logger.log('build concatenated version ...'); jake.logger.log('build concatenated version ...');
...@@ -138,9 +143,8 @@ ...@@ -138,9 +143,8 @@
}, ASYNC); }, ASYNC);
// for short test only // for short test only
desc('unit tests'); desc('unit tests (without jshint)');
task('unit', [], function () { task('unit', [], function () {
// here we want the default reporter and not the configured one
nodeunit.reporters['default'].run(UNIT_TESTS, NODEUNIT_OPTIONS, complete); nodeunit.reporters['default'].run(UNIT_TESTS, NODEUNIT_OPTIONS, complete);
}, ASYNC); }, ASYNC);
...@@ -148,7 +152,6 @@ ...@@ -148,7 +152,6 @@
task('build', [TARGET_COMPRESSED], function () { task('build', [TARGET_COMPRESSED], function () {
jake.logger.log('done.'); jake.logger.log('done.');
}); });
task('default', ['clean', 'release']); task('default', ['clean', 'build']);
}()); }());
\ No newline at end of file
...@@ -9,6 +9,8 @@ It exposes a constructor function UriTemplate with the two methods: ...@@ -9,6 +9,8 @@ It exposes a constructor function UriTemplate with the two methods:
* (static) parse(uriTemplateText) returning an instance of UriTemplate * (static) parse(uriTemplateText) returning an instance of UriTemplate
* expand(variables) returning an string * expand(variables) returning an string
Be aware, that a parsed UriTemplate is frozen, so it is stateless. You can reuse instances of UriTemplates.
Requirements Requirements
------------ ------------
...@@ -60,6 +62,7 @@ MIT License, see http://mit-license.org/ ...@@ -60,6 +62,7 @@ MIT License, see http://mit-license.org/
Release Notes Release Notes
------------- -------------
* 0.2.4 fixed double encoding according [RubenVerborgh] and some Prefix modifiers bugs
* 0.2.3 fixed bug with empty objects ('{?empty}' with '{empty:{}}' shall expand to '?empty=') * 0.2.3 fixed bug with empty objects ('{?empty}' with '{empty:{}}' shall expand to '?empty=')
* 0.2.2 fixed pct encoding bug with multibyte utf8 chars * 0.2.2 fixed pct encoding bug with multibyte utf8 chars
* 0.2.1 fixed a bug in package.json * 0.2.1 fixed a bug in package.json
...@@ -67,7 +70,6 @@ Release Notes ...@@ -67,7 +70,6 @@ Release Notes
Next Steps Next Steps
---------- ----------
* Implementing unit tests (now only dummy test implemented) * Implementing more unit tests (now only a view tests are implemented)
* Updating uritemplate-test (mnot added some new tests and removed some wrong. At the moment I cannot update, because the new tests will not pass)
* A new method extract(uri), which tries to extract the variables from a given uri. * A new method extract(uri), which tries to extract the variables from a given uri.
This is harder, than you might think This is harder, than you might think
This diff is collapsed.
This diff is collapsed.
...@@ -5,13 +5,13 @@ module.exports = (function () { ...@@ -5,13 +5,13 @@ module.exports = (function () {
fs = require('fs'), fs = require('fs'),
async = require('async'); async = require('async');
function readFileUtf8(filename, callback) { function readFileUtf8 (filename, callback) {
// if you call readFile with encoding, the result is the file content as string. // if you call readFile with encoding, the result is the file content as string.
// without encoding it would be a stream, which can be converted to a string with its toString() method // without encoding it would be a stream, which can be converted to a string with its toString() method
fs.readFile(filename, 'utf-8', callback); fs.readFile(filename, 'utf-8', callback);
} }
function concat(inputFileArr, outputfile, callback, options) { function concat (inputFileArr, outputfile, callback, options) {
async.map(inputFileArr, readFileUtf8, function (err, contents) { async.map(inputFileArr, readFileUtf8, function (err, contents) {
if (err) { if (err) {
throw new Error('could not read files: ' + err); throw new Error('could not read files: ' + err);
...@@ -41,19 +41,19 @@ module.exports = (function () { ...@@ -41,19 +41,19 @@ module.exports = (function () {
}); });
} }
function startsWith(text, beginning) { function startsWith (text, beginning) {
return text.substr(0, beginning.length) === beginning; return text.substr(0, beginning.length) === beginning;
} }
function removeFirstLine(text) { function removeFirstLine (text) {
var indexOfLinebreak = text.indexOf("\n"); var indexOfLinebreak = text.indexOf("\n");
if (indexOfLinebreak < 0) { if (indexOfLinebreak < 0) {
return ""; return text;
} }
return text.substr(indexOfLinebreak + 1); return text.substr(indexOfLinebreak + 1);
} }
function removeUseStrict(text) { function removeUseStrict (text) {
var lines = text.split('\n'); var lines = text.split('\n');
var filteredLines = lines.filter(function (line) { var filteredLines = lines.filter(function (line) {
return line.indexOf('"use strict"') < 0; return line.indexOf('"use strict"') < 0;
...@@ -61,7 +61,7 @@ module.exports = (function () { ...@@ -61,7 +61,7 @@ module.exports = (function () {
return filteredLines.join('\n'); return filteredLines.join('\n');
} }
function removeJshintOptions(source) { function removeJshintOptions (source) {
if (startsWith(source, '/*jshint')) { if (startsWith(source, '/*jshint')) {
source = removeFirstLine(source); source = removeFirstLine(source);
} }
......
...@@ -22,7 +22,7 @@ module.exports = (function () { ...@@ -22,7 +22,7 @@ module.exports = (function () {
undef: true, // forbids use of undefined variables undef: true, // forbids use of undefined variables
unused: true, // forbids unused variables unused: true, // forbids unused variables
maxcomplexity: 17 // much too high. should be max. 10 maxcomplexity: 19 // much too high. should be max. 10
}, },
JSHINT_GLOBALS = { JSHINT_GLOBALS = {
...@@ -39,13 +39,19 @@ module.exports = (function () { ...@@ -39,13 +39,19 @@ module.exports = (function () {
return; return;
} }
if (err) { if (err) {
throw new Error('could not read file ' + jsFile + ': ' + err); callback('could not read file ' + jsFile + ': ' + err);
return;
} }
numCheckedFiles += 1; numCheckedFiles += 1;
if (!jshint.jshint(content, JSHINT_OPTIONS, JSHINT_GLOBALS)) { if (!jshint.jshint(content, JSHINT_OPTIONS, JSHINT_GLOBALS)) {
failed = true; failed = true;
console.log(jshint.errors); jshint.errors.push({filename: jsFile});
callback(jshint.errors, jsFile); // jshint errors are awfully formated, when given to jake.fail()
console.log(JSON.stringify(jshint.errors, null, 4));
callback({
filename: jsFile,
reason: jshint.errors[0].reason
});
} }
if (numCheckedFiles === jsFiles.length) { if (numCheckedFiles === jsFiles.length) {
callback(); callback();
......
...@@ -8,5 +8,40 @@ ...@@ -8,5 +8,40 @@
"testcases" : [ "testcases" : [
["http://localhost:8080/api/search{?q}", "http://localhost:8080/api/search?q=val"] ["http://localhost:8080/api/search{?q}", "http://localhost:8080/api/search?q=val"]
] ]
},
"Allowed characters": {
"level": 3,
"variables": {
"dot": "dot"
},
"testcases": [
["{?.dot}", false],
["{a,.b}", false]
]
},
"Prefix modifiers": {
"level": 4,
"variables": {
"text": "I like octal numbers",
"emptyObject": {},
"filledObject": {"composite": "true"},
"emptyArr": [],
"filledArr": [1]
},
"testcases": [
["{text:0}", false],
["{text:08}", false],
["{text:8}", "I%20like%20o"],
["{text:9999}", "I%20like%20octal%20numbers"],
["{text:10000}", false],
["{text:1.0}", false],
["{text:1e+2}", false],
["{text:9876543210987654321098765432109876543210}", false],
["{emptyObject:1}", ""],
["{filledObject:1}", false],
["{emptyArr:1}", ""],
["{filledArr:1}", false]
]
} }
} }
...@@ -12,12 +12,10 @@ ...@@ -12,12 +12,10 @@
"contributors": [], "contributors": [],
"dependencies": {}, "dependencies": {},
"main": "bin/uritemplate.js", "main": "bin/uritemplate.js",
"licenses": [ "licenses": [{
{
"type": "MIT", "type": "MIT",
"url": "http://www.opensource.org/licenses/mit-license.php" "url": "http://www.opensource.org/licenses/mit-license.php"
} }],
],
"files": [ "files": [
"src", "src",
"test", "test",
...@@ -27,7 +25,7 @@ ...@@ -27,7 +25,7 @@
"demo.html", "demo.html",
"demo_AMD.html", "demo_AMD.html",
"README.md", "README.md",
"reqire.js", "require.js",
".gitmodules", ".gitmodules",
"uritemplate-test/extended-tests.json", "uritemplate-test/extended-tests.json",
"uritemplate-test/negative-tests.json", "uritemplate-test/negative-tests.json",
...@@ -35,7 +33,7 @@ ...@@ -35,7 +33,7 @@
"uritemplate-test/spec-examples-by-sections.json", "uritemplate-test/spec-examples-by-sections.json",
"uritemplate-test/spec-examples.json" "uritemplate-test/spec-examples.json"
], ],
"version": "0.2.3", "version": "0.2.4",
"readmeFilename": "README.md", "readmeFilename": "README.md",
"gitHead": "901b85201a821427dfb4591b56aea3a70d45c67c", "gitHead": "901b85201a821427dfb4591b56aea3a70d45c67c",
"devDependencies": { "devDependencies": {
...@@ -48,6 +46,5 @@ ...@@ -48,6 +46,5 @@
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/fxa/uritemplate-js.git" "url": "https://github.com/fxa/uritemplate-js.git"
}, }
"license": "MIT"
} }
...@@ -2,25 +2,28 @@ ...@@ -2,25 +2,28 @@
/*global pctEncoder, rfcCharHelper*/ /*global pctEncoder, rfcCharHelper*/
var LiteralExpression = (function () { var LiteralExpression = (function () {
"use strict"; "use strict";
function LiteralExpression (literal) {
this.literal = LiteralExpression.encodeLiteral(literal);
}
function encodeLiteral(literal) { LiteralExpression.encodeLiteral = function (literal) {
var var
result = '', result = '',
index, index,
chr = ''; chr = '';
for (index = 0; index < literal.length; index += chr.length) { for (index = 0; index < literal.length; index += chr.length) {
chr = literal.charAt(index); chr = pctEncoder.pctCharAt(literal, index);
if (chr.length > 1) {
result += chr;
}
else {
result += rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr); result += rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr);
} }
return result; // chr = literal.charAt(index);
// result += rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr);
} }
return result;
function LiteralExpression(literal) { };
this.literal = LiteralExpression.encodeLiteral(literal);
}
LiteralExpression.encodeLiteral = encodeLiteral;
LiteralExpression.prototype.expand = function () { LiteralExpression.prototype.expand = function () {
return this.literal; return this.literal;
......
/*jshint unused:false */ /*jshint unused:false */
/*global parse*/ /*global parse, objectHelper*/
var UriTemplate = (function () { var UriTemplate = (function () {
"use strict"; "use strict";
function UriTemplate(templateText, expressions) { function UriTemplate (templateText, expressions) {
this.templateText = templateText; this.templateText = templateText;
this.expressions = expressions; this.expressions = expressions;
objectHelper.deepFreeze(this);
} }
UriTemplate.prototype.toString = function () { UriTemplate.prototype.toString = function () {
...@@ -12,6 +13,7 @@ var UriTemplate = (function () { ...@@ -12,6 +13,7 @@ var UriTemplate = (function () {
}; };
UriTemplate.prototype.expand = function (variables) { UriTemplate.prototype.expand = function (variables) {
// this.expressions.map(function (expression) {return expression.expand(variables);}).join('');
var var
index, index,
result = ''; result = '';
...@@ -22,6 +24,5 @@ var UriTemplate = (function () { ...@@ -22,6 +24,5 @@ var UriTemplate = (function () {
}; };
UriTemplate.parse = parse; UriTemplate.parse = parse;
return UriTemplate; return UriTemplate;
}()); }());
...@@ -2,13 +2,12 @@ ...@@ -2,13 +2,12 @@
/*global pctEncoder, rfcCharHelper, isDefined, LiteralExpression, objectHelper*/ /*global pctEncoder, rfcCharHelper, isDefined, LiteralExpression, objectHelper*/
var VariableExpression = (function () { var VariableExpression = (function () {
"use strict"; "use strict";
// helper function if JSON is not available // helper function if JSON is not available
function prettyPrint(value) { function prettyPrint (value) {
return JSON ? JSON.stringify(value) : value; return JSON ? JSON.stringify(value) : value;
} }
function VariableExpression(templateText, operator, varspecs) { function VariableExpression (templateText, operator, varspecs) {
this.templateText = templateText; this.templateText = templateText;
this.operator = operator; this.operator = operator;
this.varspecs = varspecs; this.varspecs = varspecs;
...@@ -18,7 +17,7 @@ var VariableExpression = (function () { ...@@ -18,7 +17,7 @@ var VariableExpression = (function () {
return this.templateText; return this.templateText;
}; };
VariableExpression.prototype.expand = function expandExpression(variables) { VariableExpression.prototype.expand = function (variables) {
var var
result = '', result = '',
index, index,
...@@ -29,7 +28,7 @@ var VariableExpression = (function () { ...@@ -29,7 +28,7 @@ var VariableExpression = (function () {
operator = this.operator; operator = this.operator;
// callback to be used within array.reduce // callback to be used within array.reduce
function reduceUnexploded(result, currentValue, currentKey) { function reduceUnexploded (result, currentValue, currentKey) {
if (isDefined(currentValue)) { if (isDefined(currentValue)) {
if (result.length > 0) { if (result.length > 0) {
result += ','; result += ',';
...@@ -42,7 +41,7 @@ var VariableExpression = (function () { ...@@ -42,7 +41,7 @@ var VariableExpression = (function () {
return result; return result;
} }
function reduceNamedExploded(result, currentValue, currentKey) { function reduceNamedExploded (result, currentValue, currentKey) {
if (isDefined(currentValue)) { if (isDefined(currentValue)) {
if (result.length > 0) { if (result.length > 0) {
result += operator.separator; result += operator.separator;
...@@ -53,7 +52,7 @@ var VariableExpression = (function () { ...@@ -53,7 +52,7 @@ var VariableExpression = (function () {
return result; return result;
} }
function reduceUnnamedExploded(result, currentValue, currentKey) { function reduceUnnamedExploded (result, currentValue, currentKey) {
if (isDefined(currentValue)) { if (isDefined(currentValue)) {
if (result.length > 0) { if (result.length > 0) {
result += operator.separator; result += operator.separator;
...@@ -91,7 +90,7 @@ var VariableExpression = (function () { ...@@ -91,7 +90,7 @@ var VariableExpression = (function () {
} }
result += '='; result += '=';
} }
if (varspec.maxLength && value.length > varspec.maxLength) { if (varspec.maxLength !== null) {
value = value.substr(0, varspec.maxLength); value = value.substr(0, varspec.maxLength);
} }
result += operator.encode(value); result += operator.encode(value);
...@@ -135,6 +134,5 @@ var VariableExpression = (function () { ...@@ -135,6 +134,5 @@ var VariableExpression = (function () {
return result; return result;
}; };
return VariableExpression; return VariableExpression;
}()); }());
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* @param object * @param object
* @return {Boolean} * @return {Boolean}
*/ */
function isDefined(object) { function isDefined (object) {
"use strict"; "use strict";
var var
index, index,
......
/*jshint unused: false */ /*jshint unused: false */
var objectHelper = (function () { var objectHelper = (function () {
"use strict"; "use strict";
function isArray (value) {
function isArray(value) {
return Object.prototype.toString.apply(value) === '[object Array]'; return Object.prototype.toString.apply(value) === '[object Array]';
} }
// performs an array.reduce for objects // performs an array.reduce for objects
// TODO handling if initialValue is undefined // TODO handling if initialValue is undefined
function objectReduce(object, callback, initialValue) { function objectReduce (object, callback, initialValue) {
var var
propertyName, propertyName,
currentValue = initialValue; currentValue = initialValue;
...@@ -22,7 +21,7 @@ var objectHelper = (function () { ...@@ -22,7 +21,7 @@ var objectHelper = (function () {
// performs an array.reduce, if reduce is not present (older browser...) // performs an array.reduce, if reduce is not present (older browser...)
// TODO handling if initialValue is undefined // TODO handling if initialValue is undefined
function arrayReduce(array, callback, initialValue) { function arrayReduce (array, callback, initialValue) {
var var
index, index,
currentValue = initialValue; currentValue = initialValue;
...@@ -32,12 +31,39 @@ var objectHelper = (function () { ...@@ -32,12 +31,39 @@ var objectHelper = (function () {
return currentValue; return currentValue;
} }
function reduce(arrayOrObject, callback, initialValue) { function reduce (arrayOrObject, callback, initialValue) {
return isArray(arrayOrObject) ? arrayReduce(arrayOrObject, callback, initialValue) : objectReduce(arrayOrObject, callback, initialValue); return isArray(arrayOrObject) ? arrayReduce(arrayOrObject, callback, initialValue) : objectReduce(arrayOrObject, callback, initialValue);
} }
function deepFreezeUsingObjectFreeze (object) {
if (typeof object !== "object" || object === null) {
return object;
}
Object.freeze(object);
var property, propertyName;
for (propertyName in object) {
if (object.hasOwnProperty(propertyName)) {
property = object[propertyName];
// be aware, arrays are 'object', too
if (typeof property === "object") {
deepFreeze(property);
}
}
}
return object;
}
function deepFreeze (object) {
if (typeof Object.freeze === 'function') {
return deepFreezeUsingObjectFreeze(object);
}
return object;
}
return { return {
isArray: isArray, isArray: isArray,
reduce: reduce reduce: reduce,
deepFreeze: deepFreeze
}; };
}()); }());
/*jshint unused:false */ /*jshint unused:false */
/*global pctEncoder, operators, charHelper, rfcCharHelper, LiteralExpression, UriTemplate, VariableExpression */ /*global pctEncoder, operators, charHelper, rfcCharHelper, LiteralExpression, UriTemplate, VariableExpression*/
var parse = (function () { var parse = (function () {
"use strict"; "use strict";
function parseExpression (outerText) {
function parseExpression(outerText) {
var var
text, text,
operator, operator,
...@@ -14,12 +13,12 @@ var parse = (function () { ...@@ -14,12 +13,12 @@ var parse = (function () {
index, index,
chr = ''; chr = '';
function closeVarname() { function closeVarname () {
varspec = {varname: text.substring(varnameStart, index), exploded: false, maxLength: null}; varspec = {varname: text.substring(varnameStart, index), exploded: false, maxLength: null};
varnameStart = null; varnameStart = null;
} }
function closeMaxLength() { function closeMaxLength () {
if (maxLengthStart === index) { if (maxLengthStart === index) {
throw new Error("after a ':' you have to specify the length. position = " + index); throw new Error("after a ':' you have to specify the length. position = " + index);
} }
...@@ -27,7 +26,7 @@ var parse = (function () { ...@@ -27,7 +26,7 @@ var parse = (function () {
maxLengthStart = null; maxLengthStart = null;
} }
// remove outer {} // remove outer braces
text = outerText.substr(1, outerText.length - 2); text = outerText.substr(1, outerText.length - 2);
// determine operator // determine operator
...@@ -52,7 +51,13 @@ var parse = (function () { ...@@ -52,7 +51,13 @@ var parse = (function () {
closeVarname(); closeVarname();
} }
if (maxLengthStart !== null) { if (maxLengthStart !== null) {
if (index === maxLengthStart && chr === '0') {
throw new Error('A :prefix must not start with digit 0 -- see position ' + index);
}
if (charHelper.isDigit(chr)) { if (charHelper.isDigit(chr)) {
if (index - maxLengthStart >= 4) {
throw new Error('A :prefix must max 4 digits -- see position ' + index);
}
continue; continue;
} }
closeMaxLength(); closeMaxLength();
...@@ -96,7 +101,7 @@ var parse = (function () { ...@@ -96,7 +101,7 @@ var parse = (function () {
return new VariableExpression(outerText, operator, varspecs); return new VariableExpression(outerText, operator, varspecs);
} }
function parseTemplate(uriTemplateText) { function parseTemplate (uriTemplateText) {
// assert filled string // assert filled string
var var
index, index,
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
/*global charHelper, unescape*/ /*global charHelper, unescape*/
var pctEncoder = (function () { var pctEncoder = (function () {
"use strict"; "use strict";
var utf8 = { var utf8 = {
encode: function (chr) { encode: function (chr) {
// see http://ecmanaut.blogspot.de/2006/07/encoding-decoding-utf8-in-javascript.html // see http://ecmanaut.blogspot.de/2006/07/encoding-decoding-utf8-in-javascript.html
...@@ -47,16 +46,28 @@ var pctEncoder = (function () { ...@@ -47,16 +46,28 @@ var pctEncoder = (function () {
return result; return result;
} }
/**
* Returns, whether the given text at start is in the form 'percent hex-digit hex-digit', like '%3F'
* @param text
* @param start
* @return {boolean|*|*}
*/
function isPercentDigitDigit (text, start) { function isPercentDigitDigit (text, start) {
return text[start] === '%' && charHelper.isHexDigit(text[start + 1]) && charHelper.isHexDigit(text[start + 2]); return text[start] === '%' && charHelper.isHexDigit(text[start + 1]) && charHelper.isHexDigit(text[start + 2]);
} }
function parseHex2(text, start) { /**
* Parses a hex number from start with length 2.
* @param text a string
* @param start the start index of the 2-digit hex number
* @return {Number}
*/
function parseHex2 (text, start) {
return parseInt(text.substr(start, 2), 16); return parseInt(text.substr(start, 2), 16);
} }
/** /**
* Returns wether or not the given char sequence is a correctly pct-encoded sequence. * Returns whether or not the given char sequence is a correctly pct-encoded sequence.
* @param chr * @param chr
* @return {boolean} * @return {boolean}
*/ */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
if (typeof module !== "undefined") { if (typeof module !== "undefined") {
module.exports = UriTemplate; module.exports = UriTemplate;
} }
else if (typeof define !== "undefined") { else if (typeof define === "function") {
define([],function() { define([],function() {
return UriTemplate; return UriTemplate;
}); });
......
...@@ -8,7 +8,7 @@ var rfcCharHelper = (function () { ...@@ -8,7 +8,7 @@ var rfcCharHelper = (function () {
* @param chr * @param chr
* @return (Boolean) * @return (Boolean)
*/ */
function isVarchar(chr) { function isVarchar (chr) {
return charHelper.isAlpha(chr) || charHelper.isDigit(chr) || chr === '_' || pctEncoder.isPctEncoded(chr); return charHelper.isAlpha(chr) || charHelper.isDigit(chr) || chr === '_' || pctEncoder.isPctEncoded(chr);
} }
...@@ -17,7 +17,7 @@ var rfcCharHelper = (function () { ...@@ -17,7 +17,7 @@ var rfcCharHelper = (function () {
* @param chr * @param chr
* @return {Boolean} * @return {Boolean}
*/ */
function isUnreserved(chr) { function isUnreserved (chr) {
return charHelper.isAlpha(chr) || charHelper.isDigit(chr) || chr === '-' || chr === '.' || chr === '_' || chr === '~'; return charHelper.isAlpha(chr) || charHelper.isDigit(chr) || chr === '-' || chr === '.' || chr === '_' || chr === '~';
} }
...@@ -27,9 +27,9 @@ var rfcCharHelper = (function () { ...@@ -27,9 +27,9 @@ var rfcCharHelper = (function () {
* @param chr * @param chr
* @return {Boolean} * @return {Boolean}
*/ */
function isReserved(chr) { function isReserved (chr) {
return chr === ':' || chr === '/' || chr === '?' || chr === '#' || chr === '[' || chr === ']' || chr === '@' || chr === '!' || chr === '$' || chr === '&' || chr === '(' || return chr === ':' || chr === '/' || chr === '?' || chr === '#' || chr === '[' || chr === ']' || chr === '@' || chr === '!' || chr === '$' || chr === '&' || chr === '(' ||
chr === ')' || chr === '*' || chr === '+' || chr === ',' || chr === ';' || chr === '=' || chr === "'" || chr === '%'; chr === ')' || chr === '*' || chr === '+' || chr === ',' || chr === ';' || chr === '=' || chr === "'";
} }
return { return {
......
...@@ -5,9 +5,19 @@ module.exports = (function () { ...@@ -5,9 +5,19 @@ module.exports = (function () {
UriTemplate = require('../../' + global.URI_TEMPLATE_FILE); UriTemplate = require('../../' + global.URI_TEMPLATE_FILE);
return { return {
'UriTemplate has a parse function': function (test) { 'UriTemplate has a static parse function': function (test) {
test.equal(typeof UriTemplate.parse, 'function'); test.equal(typeof UriTemplate.parse, 'function');
test.done(); test.done();
},
'UriTemplate has a expand function': function (test) {
test.equal(typeof UriTemplate.prototype.expand, 'function');
test.done();
},
'UriTemplate instances are frozen': function (test) {
var ut = new UriTemplate('text', []);
test.ok(Object.isFrozen(ut));
test.ok(Object.isFrozen(ut.expressions));
test.done();
} }
}; };
}()); }());
...@@ -5,27 +5,25 @@ module.exports = (function () { ...@@ -5,27 +5,25 @@ module.exports = (function () {
fs = require('fs'), fs = require('fs'),
path = require('path'), path = require('path'),
sandbox = require('nodeunit').utils.sandbox; sandbox = require('nodeunit').utils.sandbox;
// var testCase = require('nodeunit').testCase;
var NOISY = false; var NOISY = false;
function log(text) { function log (text) {
if (NOISY) { if (NOISY) {
console.log(text); console.log(text);
} }
} }
function loadUriTemplate () {
function loadUriTemplate() {
var context = {module: {}}; var context = {module: {}};
sandbox(global.URI_TEMPLATE_FILE, context); sandbox(global.URI_TEMPLATE_FILE, context);
return context.module.exports; return context.module.exports;
} }
function loadTestFile(testFileName) { function loadTestFile (testFileName) {
return JSON.parse(fs.readFileSync(testFileName)); return JSON.parse(fs.readFileSync(testFileName));
} }
function assertMatches(test, template, variables, expected, chapterName, UriTemplate) { function assertMatches (test, template, variables, expected, chapterName, UriTemplate) {
var var
uriTemplate, uriTemplate,
actual, actual,
...@@ -34,13 +32,12 @@ module.exports = (function () { ...@@ -34,13 +32,12 @@ module.exports = (function () {
uriTemplate = UriTemplate.parse(template); uriTemplate = UriTemplate.parse(template);
} }
catch (error) { catch (error) {
// if expected === false, the error was expected!
if (expected === false) { if (expected === false) {
log('ok. expected error found'); log('ok. expected error found');
return; return;
} }
log('error', error); console.log('error', error);
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error: ' + error); test.fail('chapter ' + chapterName + ', template ' + template + ' threw error, when parsing: ' + error);
return; return;
} }
test.ok(!!uriTemplate, 'uri template could not be parsed'); test.ok(!!uriTemplate, 'uri template could not be parsed');
...@@ -51,11 +48,12 @@ module.exports = (function () { ...@@ -51,11 +48,12 @@ module.exports = (function () {
return; return;
} }
} }
catch (exception) { catch (error) {
if (expected === false) { if (expected === false) {
return; return;
} }
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error: ' + JSON.stringify(exception, null, 4)); console.log('error', error);
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error, when expanding: ' + JSON.stringify(error, null, 4));
return; return;
} }
if (expected.constructor === Array) { if (expected.constructor === Array) {
...@@ -74,11 +72,11 @@ module.exports = (function () { ...@@ -74,11 +72,11 @@ module.exports = (function () {
test.fail("actual: '" + actual + "', expected: one of " + JSON.stringify(expected) + ', chapter ' + chapterName + ', template ' + template); test.fail("actual: '" + actual + "', expected: one of " + JSON.stringify(expected) + ', chapter ' + chapterName + ', template ' + template);
} }
else { else {
test.equal(actual, expected, 'actual: ' + actual + ', expected: ' + expected + ', template: ' + template); test.equal(actual, expected, 'actual: "' + actual + '", expected: "' + expected + '", template: "' + template + '"');
} }
} }
function runTestFile(test, filename) { function runTestFile (test, filename) {
var var
tests, tests,
chapterName, chapterName,
...@@ -107,12 +105,15 @@ module.exports = (function () { ...@@ -107,12 +105,15 @@ module.exports = (function () {
test.done(); test.done();
} }
var SPEC_HOME = 'uritemplate-test'; var SPEC_HOME = '../uritemplate-test';
return { return {
'spec examples': function (test) { 'spec examples': function (test) {
runTestFile(test, path.join(SPEC_HOME, 'spec-examples.json')); runTestFile(test, path.join(SPEC_HOME, 'spec-examples.json'));
}, },
'spec examples by section': function (test) {
runTestFile(test, path.join(SPEC_HOME, 'spec-examples-by-section.json'));
},
'extended tests': function (test) { 'extended tests': function (test) {
runTestFile(test, path.join(SPEC_HOME, 'extended-tests.json')); runTestFile(test, path.join(SPEC_HOME, 'extended-tests.json'));
}, },
...@@ -121,6 +122,10 @@ module.exports = (function () { ...@@ -121,6 +122,10 @@ module.exports = (function () {
}, },
'own tests': function (test) { 'own tests': function (test) {
runTestFile(test, 'own-testcases.json'); runTestFile(test, 'own-testcases.json');
},
'testfile': function (test) {
test.ok(global.URI_TEMPLATE_FILE.substr(0, 3), 'tmp');
test.done();
} }
}; };
}()); }());
module.exports = (function () {
"use strict";
var
sandbox = require('nodeunit').utils.sandbox,
context = {};
sandbox('src/charHelper.js', context);
sandbox('src/pctEncoder.js', context);
sandbox('src/rfcCharHelper.js', context);
sandbox('src/encodingHelper.js', context);
sandbox('src/LiteralExpression.js', context);
var LiteralExpression = context.LiteralExpression;
return {
'LiteralExpression.encodeLiteral works as expected': function (test) {
test.equal(typeof LiteralExpression.encodeLiteral, 'function');
test.equal(LiteralExpression.encodeLiteral('a b'), 'a%20b');
test.equal(LiteralExpression.encodeLiteral('a%20b'), 'a%20b');
test.done();
}
};
}());
\ No newline at end of file
module.exports = (function () { module.exports = (function () {
"use strict"; "use strict";
var context = {console: console}; var context = {};
require('nodeunit').utils.sandbox('src/objectHelper.js', context); require('nodeunit').utils.sandbox('src/objectHelper.js', context);
var objectHelper = context.objectHelper; var objectHelper = context.objectHelper;
...@@ -29,6 +29,33 @@ module.exports = (function () { ...@@ -29,6 +29,33 @@ module.exports = (function () {
}, -1); }, -1);
test.equal(result, 17); test.equal(result, 17);
test.done(); test.done();
},
'deepFreeze': {
'deepFreeze freezes an object': function (test) {
var object = {};
objectHelper.deepFreeze(object);
test.ok(Object.isFrozen(object));
test.done();
},
'deepFreeze freezes children': function (test) {
var object = {child: {}};
objectHelper.deepFreeze(object);
test.ok(Object.isFrozen(object));
test.ok(Object.isFrozen(object.child));
test.done();
},
'deepFreeze works with arrays': function (test) {
var object = {child: []};
objectHelper.deepFreeze(object);
test.ok(Object.isFrozen(object));
test.ok(Object.isFrozen(object.child));
test.done();
},
'deepFreeze works with null': function (test) {
objectHelper.deepFreeze(null);
test.done();
}
} }
}; };
}()); }());
...@@ -3,7 +3,7 @@ module.exports = (function () { ...@@ -3,7 +3,7 @@ module.exports = (function () {
var var
sandbox = require('nodeunit').utils.sandbox, sandbox = require('nodeunit').utils.sandbox,
context = {console: console}; context = {};
sandbox('src/charHelper.js', context); sandbox('src/charHelper.js', context);
sandbox('src/pctEncoder.js', context); sandbox('src/pctEncoder.js', context);
var charHelper = context.charHelper; var charHelper = context.charHelper;
...@@ -62,24 +62,7 @@ module.exports = (function () { ...@@ -62,24 +62,7 @@ module.exports = (function () {
test.equal(pctEncoder.encodeCharacter('!'), '%21'); test.equal(pctEncoder.encodeCharacter('!'), '%21');
test.done(); test.done();
} }
},
'this and that': {
"ouml is correct encoded": function (test) {
test.equal(pctEncoder.encodeCharacter('ö'), '%C3%B6');
test.done();
},
'ouml is pctEncoded': function (test) {
test.ok(pctEncoder.isPctEncoded('%C3%B6'));
test.done();
},
'ouml is extracted from text': function (test) {
test.equal(pctEncoder.pctCharAt('zw%C3%B6lf', 2), '%C3%B6');
test.done();
} }
}
}; };
}()); }());
module.exports = (function () {
"use strict";
var
sandbox = require('nodeunit').utils.sandbox,
context = {};
sandbox('src/objectHelper.js', context);
sandbox('src/charHelper.js', context);
sandbox('src/pctEncoder.js', context);
sandbox('src/rfcCharHelper.js', context);
sandbox('src/encodingHelper.js', context);
sandbox('src/operators.js', context);
sandbox('src/isDefined.js', context);
sandbox('src/LiteralExpression.js', context);
sandbox('src/parse.js', context);
sandbox('src/VariableExpression.js', context);
sandbox('src/UriTemplate_.js', context);
var
operators = context.operators,
VariableExpression = context.VariableExpression,
UriTemplate = context.UriTemplate;
return {
'UriTemplate has a static parse function': function (test) {
test.equal(typeof UriTemplate.parse, 'function');
test.done();
},
'UriTemplate has a expand function': function (test) {
test.equal(typeof UriTemplate.prototype.expand, 'function');
test.done();
},
'UriTemplate instances are frozen': function (test) {
var ut = new UriTemplate('text', []);
test.ok(Object.isFrozen(ut));
test.ok(Object.isFrozen(ut.expressions));
test.done();
}
};
}());
...@@ -3,7 +3,7 @@ module.exports = (function () { ...@@ -3,7 +3,7 @@ module.exports = (function () {
var var
sandbox = require('nodeunit').utils.sandbox, sandbox = require('nodeunit').utils.sandbox,
context = {console: console}; context = {};
sandbox('src/objectHelper.js', context); sandbox('src/objectHelper.js', context);
sandbox('src/charHelper.js', context); sandbox('src/charHelper.js', context);
sandbox('src/pctEncoder.js', context); sandbox('src/pctEncoder.js', context);
...@@ -11,6 +11,7 @@ module.exports = (function () { ...@@ -11,6 +11,7 @@ module.exports = (function () {
sandbox('src/encodingHelper.js', context); sandbox('src/encodingHelper.js', context);
sandbox('src/operators.js', context); sandbox('src/operators.js', context);
sandbox('src/isDefined.js', context); sandbox('src/isDefined.js', context);
sandbox('src/LiteralExpression.js', context);
sandbox('src/VariableExpression.js', context); sandbox('src/VariableExpression.js', context);
var operators = context.operators; var operators = context.operators;
...@@ -39,6 +40,27 @@ module.exports = (function () { ...@@ -39,6 +40,27 @@ module.exports = (function () {
]); ]);
test.equal(ve.expand({empty: {}}), ''); test.equal(ve.expand({empty: {}}), '');
test.done(); test.done();
},
"double encode if ?": function (test) {
var ve = new VariableExpression("{?uri", operators.valueOf('?'), [
{varname: 'uri', exploded: false, maxLength: null}
]);
test.equal(ve.expand({uri: 'http://example.org/?ref=http%3A%2F%2Fother.org%2F'}), '?uri=http%3A%2F%2Fexample.org%2F%3Fref%3Dhttp%253A%252F%252Fother.org%252F');
test.done();
},
"not double encode if +": function (test) {
var ve = new VariableExpression("{+uri", operators.valueOf('+'), [
{varname: 'uri', exploded: false, maxLength: null}
]);
test.equal(ve.expand({uri: 'http://example.org/?ref=http%3A%2F%2Fother.org%2F'}), 'http://example.org/?ref=http%253A%252F%252Fother.org%252F');
test.done();
},
'maxLength is used': function (test) {
var ve = new VariableExpression("{one:1}", operators.valueOf(''), [
{varname: 'one', exploded: false, maxLength: 1}
]);
test.equal(ve.expand({one: 'two'}), 't');
test.done();
} }
}; };
}()); }());
\ No newline at end of file
Subproject commit b317372c613f1aab82f1e497146b2859144b9308 Subproject commit b6d42631481d87abc6bfb950d007212991ae25ff
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment