testsuite.js revision b40f5113b5f28daabc29b6ad49c905f290cf6cb7
JSON_STRING = '[' +
'"JSON Test Pattern pass1",' +
'{"object with 1 member":["array with 1 element"]},' +
'{},' +
'[],' +
'-42,' +
'true,' +
'false,' +
'null,' +
'{' +
'"integer": 1234567890,' +
'"real": -9876.543210,' +
'"e": 0.123456789e-12,' +
'"E": 1.234567890E+34,' +
'"": 23456789012E66,' +
'"zero": 0,' +
'"one": 1,' +
'"space": " ",' +
'"quote": "\\"",' +
'"backslash": "\\\\",' +
'"controls": "\\b\\f\\n\\r\\t",' +
'"slash": "/ & \\/",' +
'"alpha": "abcdefghijklmnopqrstuvwxyz",' +
'"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",' +
'"digit": "0123456789",' +
'"0123456789": "digit",' +
'"special": "`1~!@#$%^&*()_+-={\':[,]}|;.</>?",' +
'"hex": "\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A",' +
'"true": true,' +
'"false": false,' +
'"null": null,' +
'"array":[ ],' +
'"object":{ },' +
'"address": "50 St. James Street",' +
'"url": "http://www.JSON.org/",' +
'"comment": "// /* <!-- --",' +
'"# -- --> */": " ",' +
'" s p a c e d " :[1,2 , 3,4 , 5 , 6 ,7 ],' +
'"compact":[1,2,3,4,5,6,7],' +
'"jsontext": "{\\\"object with 1 member\\\":[\\\"array with 1 element\\\"]}",' +
'"quotes": "" \\u0022 %22 0x22 034 "",' +
'"\\/\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?" : "A key can be any string"' +
'},' +
'0.5 ,98.6,' +
'99.44,' +
'1066,' +
'1e1,' +
'0.1e1,' +
'1e-1,' +
'1e00,' +
'2e+00,' +
'2e-00,' +
'"rosebud"]';
/*****************************/
/* Tests begin here */
/*****************************/
// wrapped in a function to allow repeating tests with native behavior disabled
function addTests() {
name : "parse",
_should : {
error : {
test_failOnEmptyString : true,
test_failOnFunction : true,
test_failOnRegex : true,
test_failOnNew : true,
test_failOnUnquotedVal : true,
test_failOnUnquotedKey : true,
test_failOnUnclosedObject : true,
test_failOnUnclosedArray : true,
test_failOnExtraCommaInObject : true,
test_failOnExtraCommaInArray : true,
test_failOnMissingValue : true,
test_failOnCommaAfterClose : true,
test_failOnValueAfterClose : true,
test_failOnExtraClose : true,
test_failOnExpression : true,
test_failOnZeroPrefixedNumber : true,
test_failOnHex : true,
test_failOnMissingColon : true,
test_failOnDoubleColon : true,
test_failOnCommaInsteadOfColon : true,
test_failOnColonInsteadOfComma : true,
test_failOnSingleQuote : true,
test_failOnTabCharacter : true,
test_failOnLineBreakChar : true,
test_failOnMismatchedClose : true
}
},
test_emptyObject : function () {
},
test_emptyArray : function () {
},
test_JSONNatives : function () {
// Note: backslashes are double escaped to emulate string returned from
// server.
var data = Y.JSON.parse('{"obj":{},"arr":[],"f":false,"t":true,"n":null,"int":12345,"fl":1.2345,"str":"String\\nwith\\tescapes"}');
},
test_basics : function () {
Y.Assert.areSame("A key can be any string",data[8]["\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]);
},
test_nonObjectWrapper : function () {
//Y.Assert.areSame('this is a string',Y.JSON.parse('"this is a string"'));
//Y.Assert.areSame(true,Y.JSON.parse('true'));
//Y.Assert.areSame(12345,Y.JSON.parse("12345"));
//Y.Assert.areSame(1.2345,Y.JSON.parse("1.2345"));
},
test_failOnEmptyString : function () {
// parse should throw an error
},
test_failOnFunction : function () {
// parse should throw an error
},
test_failOnRegex : function () {
// parse should throw an error
},
test_failOnNew : function () {
// parse should throw an error
},
test_failOnUnquotedVal : function () {
// parse should throw an error
},
test_failOnUnquotedKey : function () {
// parse should throw an error
},
test_failOnUnclosedObject : function () {
// parse should throw an error
},
test_failOnUnclosedArray : function () {
// parse should throw an error
},
test_failOnExtraCommaInObject : function () {
// JS validator will allow, FF 3.1b2 native will allow. IE8 errors.
// eval will fail in IE6-7, but pass in others
// Trailing commas are invalid, but not a security risk, so acceptable
throw new Error("Parsed object with extra comma, but should have failed.");
},
test_failOnDoubleExtraCommaInObject : function () {
// parse should throw an error
},
test_failOnExtraCommaInArray : function () {
// Correct failure in IE6-8. FF accepts trailing commas without error
// Trailing commas are invalid, but not a security risk, so acceptable
throw new Error("Parsed array with extra comma, but should have failed.");
},
test_failOnDoubleExtraCommaInArray : function () {
// Correct failure in IE6-8. FF accepts trailing commas without error
// Trailing commas are invalid, but not a security risk, so acceptable
throw new Error("Parsed array with two extra commas, but should have failed.");
},
test_failOnMissingValue : function () {
// Correct failure in IE6-8. FF accepts trailing commas without error
// Trailing commas are invalid, but not a security risk, so acceptable
},
test_failOnCommaAfterClose : function () {
// parse should throw an error
},
test_failOnValueAfterClose : function () {
// parse should throw an error
},
test_failOnExtraClose : function () {
// parse should throw an error
throw new Error("Parsed extra closing curly brace on object, but should have failed.");
},
test_failOnExpression : function () {
// parse should throw an error
},
test_failOnZeroPrefixedNumber : function () {
// Correct failure in IE8. FF accepts leading zeros without error
// Leading zeros are invalid, but not a security risk, so acceptable
throw new Error("Parsed zero prefixed number, but should have failed.");
},
test_failOnHex : function () {
// parse should throw an error
},
test_failOnIllegalBackslashEscape : function () {
// Correctly fails in all but IE8's native parse.
// The spec does not specify a limitation to the escape characters a
// decoder supports, so either is acceptable.
throw new Error("Parsed illegal backslash escape \\x15, but should have failed.");
},
test_failOnMissingColon : function () {
// parse should throw an error
},
test_failOnDoubleColon : function () {
// parse should throw an error
},
test_failOnCommaInsteadOfColon : function () {
// parse should throw an error
},
test_failOnColonInsteadOfComma : function () {
// parse should throw an error
},
test_failOnSingleQuote : function () {
// parse should throw an error
},
test_failOnLineBreakChar : function () {
// FF3.1b2 currently allows linebreak chars in native implementation
// Harmless, so permissable
throw new Error("Parsed unescaped line break character, but should have failed.");
},
test_failOnMismatchedClose : function () {
// parse should throw an error
}
}));
name : "stringify",
_should : {
error : {
test_failOnStringifyCyclicalRef1 : true,
test_failOnStringifyCyclicalRef2 : true,
}
},
setUp: function () {
'<h3>Form used for field value extraction, stringification</h3>' +
'<input type="text" id="empty_text">' +
'<input type="text" id="text" value="text">' +
'<input type="radio" name="radio" id="unchecked_radio" value="unchecked">' +
'<input type="radio" name="radio" id="checked_radio" value="radio" checked="checked">' +
'<input type="checkbox" name="box" id="unchecked_box" value="unchecked">' +
'<input type="checkbox" name="box" id="checked_box" value="box" checked="checked">' +
'<textarea id="empty_textarea"></textarea>' +
'<textarea id="textarea">textarea</textarea>' +
'<select id="select">' +
'<option value="unselected">Unselected</option>' +
'<option value="selected" selected="selected">Selected</option>' +
'</select>' +
'<select id="multiple_select" multiple="multiple" size="3">' +
'<option value="unselected">Unselected</option>' +
'<option value="selected" selected="selected">Selected</option>' +
'<option value="selected also" selected="selected">Selected also</option>' +
'</select>' +
'<button id="button" type="button">content; no value</button>' +
'<button id="button_with_value" type="button" value="button value">content and value</button>' +
'<button id="button_submit" type="submit">content; no value</button>' +
'<button id="button_submit_with_value" type="submit" value="submit button value">content and value</button>' +
'<input type="button" id="input_button" value="input button">' +
'<input type="submit" id="input_submit" value="input submit">' +
'<!--input type="image" id="input_image" src="404.png" value="input image"-->' +
'</form>');
},
tearDown: function () {
},
test_stringifyNatives: function () {
Y.Assert.areSame('[true,false,null,-0.12345,"string",{"object with one member":["array with one element"]}]',
Y.JSON.stringify([true,false,null,-0.12345,"string",{"object with one member":["array with one element"]}]));
},
test_stringifyObject : function () {
// stringify sorts the keys
Y.Assert.areSame('{"one":1,"two":true,"three":false,"four":null,"five":"String with\\nnewline","six":{"nested":-0.12345}}',
Y.JSON.stringify({one:1,two:true,three:false,four:null,five:"String with\nnewline",six : {nested:-0.12345}}));
},
test_failOnStringifyCyclicalRef1 : function () {
var o = { key: 'value' };
o.recurse = o;
// Should throw an error
},
test_failOnStringifyCyclicalRef2 : function () {
var o = [1,2,3];
o[3] = o;
// Should throw an error
},
test_failOnStringifyCyclicalRef3 : function () {
// Should throw an error
},
test_stringifyFunction : function () {
arr : [ function () {} ]
}));
},
test_stringifyRegex : function () {
arr : [ new RegExp("in array") ]
}));
},
test_stringifyUndefined : function () {
}));
},
test_stringifyDate : function () {
// native toJSON method should be called if available
},
test_stringifyFormValue : function () {
var data = {
// Buttons commented out for now because IE reports values
// differently
//button : $('button').value,
//button_with_value : $('button_with_value').value,
//button_submit : $('button_submit').value,
//button_submit_with_value: $('button_submit_with_value').value,
//input_image : $('input_image').value
};
'"empty_text":"",'+
'"text":"text",'+
'"unchecked_radio":"unchecked",'+
'"checked_radio":"radio",'+
'"unchecked_box":"unchecked",'+
'"checked_box":"box",'+
'"empty_textarea":"",'+
'"textarea":"textarea",'+
'"select":"selected",'+
'"multiple_select":"selected",'+
//'"button":"",'+
//'"button_with_value":"button value",'+
//'"button_submit":"",'+
//'"button_submit_with_value":"submit button value",'+
'"input_button":"input button",'+
'"input_submit":"input submit"}',
//'"input_image":"input image"}',
}
}));
name : "whitelist",
test_emptyWhitelistArray : function () {
1,true,null,{
foo : false,
bar : -0.12345
],[]));
},
test_whitelistArray : function () {
foo : [
1,2,3,{
foo : "FOO",
baz : true
}
],
baz : true
},["foo"]));
foo : [
1,2,3,{
foo : "FOO",
baz : true
}
],
bar : [
1,2,3,{
foo : "FOO",
baz : true
}
],
}
/*
// REMOVED for spec compatibility
test_whitelistObject : function () {
// (undocumented) supports an obj literal as well
Y.Assert.areSame('{"foo":[1,2,3,{"foo":"FOO","baz":true}],"baz":true}',
Y.JSON.stringify({
foo : [
1,2,3,{
foo : "FOO",
baz : true
}
],
bar : [
1,2,3,{
foo : "FOO",
baz : true
}
],
baz : true
}, {foo : true, baz : false})); // values ignored
}
*/
}));
name : "formatting",
test_falseyIndents : function () {
foo0 : [ 2, {
bar : [ 4, {
baz : [ 6, {
"deep enough" : 7
}]
}]
}]
},null,0));
/* Commented out because FF3.5 has infinite loop bug for neg indents
* Fixed in FF for next version.
Y.Assert.areSame('{"foo-4":[2,{"bar":[4,{"baz":[6,{"deep enough":7}]}]}]}',
Y.JSON.stringify({
"foo-4" : [ 2, {
bar : [ 4, {
baz : [ 6, {
"deep enough" : 7
}]
}]
}]
},null,-4));
*/
foo_false : [ 2, {
bar : [ 4, {
baz : [ 6, {
"deep enough" : 7
}]
}]
}]
},null,false));
foo_empty : [ 2, {
bar : [ 4, {
baz : [ 6, {
"deep enough" : 7
}]
}]
}]
},null,""));
},
test_indentNumber : function () {
" \"foo\": [\n" +
" 2,\n" +
" {\n" +
" \"bar\": [\n" +
" 4,\n" +
" {\n" +
" \"baz\": [\n" +
" 6,\n" +
" {\n" +
" \"deep enough\": 7\n" +
" }\n" +
" ]\n" +
" }\n" +
" ]\n" +
" }\n" +
" ]\n" +
"}",
foo : [ 2, {
bar : [ 4, {
baz : [ 6, {
"deep enough" : 7
}]
}]
}]
},null,2));
},
test_indentString : function () {
"Xo\"foo\": [\n" +
"XoXo2,\n" +
"XoXo{\n" +
"XoXoXo\"bar\": [\n" +
"XoXoXoXo4,\n" +
"XoXoXoXo{\n" +
"XoXoXoXoXo\"baz\": [\n" +
"XoXoXoXoXoXo6,\n" +
"XoXoXoXoXoXo{\n" +
"XoXoXoXoXoXoXo\"deep enough\": 7\n" +
"XoXoXoXoXoXo}\n" +
"XoXoXoXoXo]\n" +
"XoXoXoXo}\n" +
"XoXoXo]\n" +
"XoXo}\n" +
"Xo]\n" +
"}",
foo : [ 2, {
bar : [ 4, {
baz : [ 6, {
"deep enough" : 7
}]
}]
}]
},null,"Xo"));
}
}));
name : "reviver",
test_reviver : function () {
switch (k) {
case "alpha" : return "LOWER CASE";
case "ALPHA" : return "upper case";
case "true" :
case "false" :
case "null" : return undefined;
}
if (typeof v === 'number') {
return -(Math.abs(v|0));
}
v[99] = "NEW ITEM";
}
return v;
});
}
}));
name : "toJSON",
test_toJSON_on_object: function () {
// TODO: complex object with toJSON
},
test_toJSON_on_proto: function () {
function A() {}
function B() {}
B.prototype = new A();
function C() {
this.x = "X";
this.y = "Y";
this.z = "Z";
}
C.prototype = new B();
}
}));
name : "replacer",
test_replacer : function () {
// replacer applies to even simple value stringifications
return typeof(v) === 'number' ? v * 2 : v;
}));
// replacer is applied to every nested property as well.
// can modify the host object en route
// executes from the context of the key:value container
num: 1,
alpha: "abc",
ignore: "me",
change: "to a function",
toUpper: true,
obj: {
nested_num: 50,
alpha: "abc"
},
}
,function (k,v) {
var t = typeof v;
if (k === 'change') {
// this property should then be ignored
return function () {};
} else if (k === 'ignore') {
// this property should then be ignored
return undefined;
} else if (t === 'number') {
// undefined returned to arrays should become null
// this refers to the object containing the key:value
} else if (t === 'string' && (this.toUpper)) {
// modify the object during stringification
delete this.toUpper;
return v.toUpperCase();
} else {
return v;
}
}));
// replacer works in conjunction with indent
Y.Assert.areSame("{\n_\"num\": 2,\n_\"alpha\": \"ABC\",\n_\"obj\": {\n__\"nested_num\": 100,\n__\"alpha\": \"abc\"\n_},\n_\"arr\": [\n__2,\n__null,\n__4\n_]\n}",
num: 1,
alpha: "abc",
ignore: "me",
change: "to a function",
toUpper: true,
obj: {
nested_num: 50,
alpha: "abc"
},
}
,function (k,v) {
var t = typeof v;
if (k === 'change') {
// this property should then be ignored
return function () {};
} else if (k === 'ignore') {
// this property should then be ignored
return undefined;
} else if (t === 'number') {
// undefined returned to arrays should become null
// this refers to the object containing the key:value
} else if (t === 'string' && (this.toUpper)) {
// modify the object during stringification
delete this.toUpper;
return v.toUpperCase();
} else {
return v;
}
},'_'));
},
test_replacer_after_toJSON : function () {
function (k,v) {
return typeof v === 'string' ? v.toUpperCase() : v;
}));
// Date instances in ES5 have toJSON that outputs toISOString, which
// means the replacer should never receive a Date instance
return (v instanceof Date) ? "X" : v;
});
},
test_replacer_returning_Date : function () {
var d = new Date(),
function (k,v) {
return typeof v === 'number' ? d : v;
}));
}
}));
/*
suite.add(new Y.Test.Case({
name : "unicode",
test_ : function () {
Y.Assert.areSame();
},
}));
*/
}
// If native JSON is available, run the tests again without native calls
if (Y.JSON.useNativeParse) {
test_disableNative : function () {
Y.JSON.useNativeParse = false;
Y.JSON.useNativeStringify = false;
}
}));
addTests();
test_reenableNative : function () {
Y.JSON.useNativeParse = true;
Y.JSON.useNativeStringify = true;
}
}));
}
addTests();