Path of error is escaped where needed and looks more like actual JS.

- Escape keys looking like something complex
 - Check simple keys with simple regex, full story: http://stackoverflow.com/questions/1661197/valid-characters-for-javascript-variable-names
 - Tests
This commit is contained in:
Slava Kim
2013-08-12 15:31:07 -07:00
parent cd8248cb33
commit 2e7845af80
2 changed files with 63 additions and 8 deletions

View File

@@ -162,9 +162,7 @@ var checkSubtree = function (value, pattern) {
checkSubtree(valueElement, pattern[0]);
} catch (err) {
if (err instanceof Match.Error) {
if (err.path && err.path[0] !== '[')
err.path = "." + err.path;
err.path = "[" + index + "]" + err.path;
err.path = _prependPath(index, err.path);
}
throw err;
}
@@ -251,11 +249,8 @@ var checkSubtree = function (value, pattern) {
throw new Match.Error("Unknown key");
}
} catch (err) {
if (err instanceof Match.Error) {
if (err.path && err.path[0] !== "[")
err.path = "." + err.path;
err.path = key + err.path;
}
if (err instanceof Match.Error)
err.path = _prependPath(key, err.path);
throw err;
}
});
@@ -309,3 +304,25 @@ _.extend(ArgumentChecker.prototype, {
self.description);
}
});
var _jsKeywords = ["do", "if", "in", "for", "let", "new", "try", "var", "case",
"else", "enum", "eval", "false", "null", "this", "true", "void", "with",
"break", "catch", "class", "const", "super", "throw", "while", "yield",
"delete", "export", "import", "public", "return", "static", "switch",
"typeof", "default", "extends", "finally", "package", "private", "continue",
"debugger", "function", "arguments", "interface", "protected", "implements",
"instanceof"];
// Assumes the base of path is already escaped properly
// returns key + base
var _prependPath = function (key, base) {
if ((typeof key) === "number" || key.match(/^[0-9]+$/))
key = "[" + key + "]";
else if (!key.match(/^[a-z_$][0-9a-z_$]*$/i) || _.contains(_jsKeywords, key))
key = JSON.stringify([key]);
if (base && base[0] !== "[")
return key + '.' + base;
return key + base;
};

View File

@@ -217,3 +217,41 @@ Tinytest.add("check - argument checker", function (test) {
check(x, Boolean);
}, true, true);
});
Tinytest.add("check - Match error path", function (test) {
var match = function (value, pattern, expectedPath) {
try {
check(value, pattern);
} catch (err) {
if (err.path != expectedPath)
test.fail({
type: "match-error-path",
message: "The path of Match.Error doesn't match.",
pattern: JSON.stringify(pattern),
value: JSON.stringify(value),
path: err.path,
expectedPath: expectedPath
});
}
};
match({ foo: [ { bar: 3 }, {bar: "something"} ] }, { foo: [ { bar: Number } ] }, "foo[1].bar");
// Complicated case with arrays, $, whitespace and quotes!
match([{ $FoO: { "bar baz\n\"'": 3 } }], [{ $FoO: { "bar baz\n\"'": String } }], "[0].$FoO[\"bar baz\\n\\\"'\"]");
// Numbers only, can be accessed w/o quotes
match({ "1231": 123 }, { "1231": String }, "[1231]");
match({ "1234abcd": 123 }, { "1234abcd": String }, "[\"1234abcd\"]");
match({ $set: { people: "nice" } }, { $set: { people: [String] } }, "$set.people");
match({ _underscore: "should work" }, { _underscore: Number }, "_underscore");
// Nested array looks nice
match([[["something", "here"], []], [["string", 123]]], [[[String]]], "[1][0][1]");
// Object nested in arrays should look nice, too!
match([[[{ foo: "something" }, { foo: "here"}],
[{ foo: "asdf" }]],
[[{ foo: 123 }]]],
[[[{ foo: String }]]], "[1][0][0].foo");
// JS keyword
match({ "return": 0 }, { "return": String }, "[\"return\"]");
});