json_test.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. // Copyright 2013 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.jsonTest');
  15. goog.setTestOnly('goog.jsonTest');
  16. goog.require('goog.functions');
  17. goog.require('goog.json');
  18. goog.require('goog.testing.jsunit');
  19. goog.require('goog.userAgent');
  20. function allChars(start, end) {
  21. var sb = [];
  22. for (var i = start; i < end; i++) {
  23. sb.push(String.fromCharCode(i));
  24. }
  25. return sb.join('');
  26. }
  27. // serialization
  28. function testStringSerialize() {
  29. assertSerialize('""', '');
  30. assertSerialize('"true"', 'true');
  31. assertSerialize('"false"', 'false');
  32. assertSerialize('"null"', 'null');
  33. assertSerialize('"0"', '0');
  34. // Unicode and control characters
  35. assertSerialize('"\\n"', '\n');
  36. assertSerialize('"\\u001f"', '\x1f');
  37. assertSerialize('"\\u20ac"', '\u20AC');
  38. assertSerialize('"\\ud83d\\ud83d"', '\ud83d\ud83d');
  39. var str = allChars(0, 10000);
  40. assertEquals(str, eval(goog.json.serialize(str)));
  41. }
  42. function testNullSerialize() {
  43. assertSerialize('null', null);
  44. assertSerialize('null', undefined);
  45. assertSerialize('null', NaN);
  46. assertSerialize('0', 0);
  47. assertSerialize('""', '');
  48. assertSerialize('false', false);
  49. }
  50. function testNullPropertySerialize() {
  51. assertSerialize('{"a":null}', {'a': null});
  52. assertSerialize('{"a":null}', {'a': undefined});
  53. }
  54. function testNumberSerialize() {
  55. assertSerialize('0', 0);
  56. assertSerialize('12345', 12345);
  57. assertSerialize('-12345', -12345);
  58. assertSerialize('0.1', 0.1);
  59. // the leading zero may not be omitted
  60. assertSerialize('0.1', .1);
  61. // no leading +
  62. assertSerialize('1', +1);
  63. // either format is OK
  64. var s = goog.json.serialize(1e50);
  65. assertTrue(
  66. '1e50', s == '1e50' || s == '1E50' || s == '1e+50' || s == '1E+50');
  67. // either format is OK
  68. s = goog.json.serialize(1e-50);
  69. assertTrue('1e50', s == '1e-50' || s == '1E-50');
  70. // These numbers cannot be represented in JSON
  71. assertSerialize('null', NaN);
  72. assertSerialize('null', Infinity);
  73. assertSerialize('null', -Infinity);
  74. }
  75. function testBooleanSerialize() {
  76. assertSerialize('true', true);
  77. assertSerialize('"true"', 'true');
  78. assertSerialize('false', false);
  79. assertSerialize('"false"', 'false');
  80. }
  81. function testArraySerialize() {
  82. assertSerialize('[]', []);
  83. assertSerialize('[1]', [1]);
  84. assertSerialize('[1,2]', [1, 2]);
  85. assertSerialize('[1,2,3]', [1, 2, 3]);
  86. assertSerialize('[[]]', [[]]);
  87. assertSerialize('[null,null]', [function() {}, function() {}]);
  88. assertNotEquals('{length:0}', goog.json.serialize({length: 0}), '[]');
  89. }
  90. function testFunctionSerialize() {
  91. assertSerialize('null', function() {});
  92. }
  93. function testObjectSerialize_emptyObject() {
  94. assertSerialize('{}', {});
  95. }
  96. function testObjectSerialize_oneItem() {
  97. assertSerialize('{"a":"b"}', {a: 'b'});
  98. }
  99. function testObjectSerialize_twoItems() {
  100. assertEquals(
  101. '{"a":"b","c":"d"}', goog.json.serialize({a: 'b', c: 'd'}),
  102. '{"a":"b","c":"d"}');
  103. }
  104. function testObjectSerialize_whitespace() {
  105. assertSerialize('{" ":" "}', {' ': ' '});
  106. }
  107. function testSerializeSkipFunction() {
  108. var object =
  109. {s: 'string value', b: true, i: 100, f: function() { var x = 'x'; }};
  110. assertSerialize('null', object.f);
  111. assertSerialize('{"s":"string value","b":true,"i":100}', object);
  112. }
  113. function testObjectSerialize_array() {
  114. assertNotEquals('[0,1]', goog.json.serialize([0, 1]), '{"0":"0","1":"1"}');
  115. }
  116. function testObjectSerialize_recursion() {
  117. if (goog.userAgent.WEBKIT) {
  118. return; // this makes safari 4 crash.
  119. }
  120. var anObject = {};
  121. anObject.thisObject = anObject;
  122. assertThrows('expected recursion exception', function() {
  123. goog.json.serialize(anObject);
  124. });
  125. }
  126. function testObjectSerializeWithHasOwnProperty() {
  127. var object = {'hasOwnProperty': null};
  128. if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) {
  129. assertEquals('{}', goog.json.serialize(object));
  130. } else {
  131. assertEquals('{"hasOwnProperty":null}', goog.json.serialize(object));
  132. }
  133. }
  134. function testWrappedObjects() {
  135. assertSerialize('"foo"', new String('foo'));
  136. assertSerialize('42', new Number(42));
  137. assertSerialize('null', new Number('a NaN'));
  138. assertSerialize('true', new Boolean(true));
  139. }
  140. // parsing
  141. function testStringParse() {
  142. assertEquals('Empty string', goog.json.parse('""'), '');
  143. assertEquals('whitespace string', goog.json.parse('" "'), ' ');
  144. // unicode without the control characters 0x00 - 0x1f, 0x7f - 0x9f
  145. var str = allChars(32, 1000);
  146. var jsonString = goog.json.serialize(str);
  147. var a = eval(jsonString);
  148. assertEquals('unicode string', goog.json.parse(jsonString), a);
  149. assertEquals('true as a string', goog.json.parse('"true"'), 'true');
  150. assertEquals('false as a string', goog.json.parse('"false"'), 'false');
  151. assertEquals('null as a string', goog.json.parse('"null"'), 'null');
  152. assertEquals('number as a string', goog.json.parse('"0"'), '0');
  153. }
  154. function testStringUnsafeParse() {
  155. assertEquals('Empty string', goog.json.unsafeParse('""'), '');
  156. assertEquals('whitespace string', goog.json.unsafeParse('" "'), ' ');
  157. // unicode
  158. var str = allChars(32, 1000);
  159. var jsonString = goog.json.serialize(str);
  160. var a = eval(jsonString);
  161. assertEquals('unicode string', goog.json.unsafeParse(jsonString), a);
  162. assertEquals('true as a string', goog.json.unsafeParse('"true"'), 'true');
  163. assertEquals('false as a string', goog.json.unsafeParse('"false"'), 'false');
  164. assertEquals('null as a string', goog.json.unsafeParse('"null"'), 'null');
  165. assertEquals('number as a string', goog.json.unsafeParse('"0"'), '0');
  166. }
  167. function testNullParse() {
  168. assertEquals('null', goog.json.parse(null), null);
  169. assertEquals('null', goog.json.parse('null'), null);
  170. assertNotEquals('0', goog.json.parse('0'), null);
  171. assertNotEquals('""', goog.json.parse('""'), null);
  172. assertNotEquals('false', goog.json.parse('false'), null);
  173. }
  174. function testNullUnsafeParse() {
  175. assertEquals('null', goog.json.unsafeParse(null), null);
  176. assertEquals('null', goog.json.unsafeParse('null'), null);
  177. assertNotEquals('0', goog.json.unsafeParse('0'), null);
  178. assertNotEquals('""', goog.json.unsafeParse('""'), null);
  179. assertNotEquals('false', goog.json.unsafeParse('false'), null);
  180. }
  181. function testNumberParse() {
  182. assertEquals('0', goog.json.parse('0'), 0);
  183. assertEquals('12345', goog.json.parse('12345'), 12345);
  184. assertEquals('-12345', goog.json.parse('-12345'), -12345);
  185. assertEquals('0.1', goog.json.parse('0.1'), 0.1);
  186. // either format is OK
  187. assertEquals(1e50, goog.json.parse('1e50'));
  188. assertEquals(1e50, goog.json.parse('1E50'));
  189. assertEquals(1e50, goog.json.parse('1e+50'));
  190. assertEquals(1e50, goog.json.parse('1E+50'));
  191. // either format is OK
  192. assertEquals(1e-50, goog.json.parse('1e-50'));
  193. assertEquals(1e-50, goog.json.parse('1E-50'));
  194. }
  195. function testNumberUnsafeParse() {
  196. assertEquals('0', goog.json.unsafeParse('0'), 0);
  197. assertEquals('12345', goog.json.unsafeParse('12345'), 12345);
  198. assertEquals('-12345', goog.json.unsafeParse('-12345'), -12345);
  199. assertEquals('0.1', goog.json.unsafeParse('0.1'), 0.1);
  200. // either format is OK
  201. assertEquals(1e50, goog.json.unsafeParse('1e50'));
  202. assertEquals(1e50, goog.json.unsafeParse('1E50'));
  203. assertEquals(1e50, goog.json.unsafeParse('1e+50'));
  204. assertEquals(1e50, goog.json.unsafeParse('1E+50'));
  205. // either format is OK
  206. assertEquals(1e-50, goog.json.unsafeParse('1e-50'));
  207. assertEquals(1e-50, goog.json.unsafeParse('1E-50'));
  208. }
  209. function testBooleanParse() {
  210. assertEquals('true', goog.json.parse('true'), true);
  211. assertEquals('false', goog.json.parse('false'), false);
  212. assertNotEquals('0', goog.json.parse('0'), false);
  213. assertNotEquals('"false"', goog.json.parse('"false"'), false);
  214. assertNotEquals('null', goog.json.parse('null'), false);
  215. assertNotEquals('1', goog.json.parse('1'), true);
  216. assertNotEquals('"true"', goog.json.parse('"true"'), true);
  217. assertNotEquals('{}', goog.json.parse('{}'), true);
  218. assertNotEquals('[]', goog.json.parse('[]'), true);
  219. }
  220. function testBooleanUnsafeParse() {
  221. assertEquals('true', goog.json.unsafeParse('true'), true);
  222. assertEquals('false', goog.json.unsafeParse('false'), false);
  223. assertNotEquals('0', goog.json.unsafeParse('0'), false);
  224. assertNotEquals('"false"', goog.json.unsafeParse('"false"'), false);
  225. assertNotEquals('null', goog.json.unsafeParse('null'), false);
  226. assertNotEquals('1', goog.json.unsafeParse('1'), true);
  227. assertNotEquals('"true"', goog.json.unsafeParse('"true"'), true);
  228. assertNotEquals('{}', goog.json.unsafeParse('{}'), true);
  229. assertNotEquals('[]', goog.json.unsafeParse('[]'), true);
  230. }
  231. function testArrayParse() {
  232. assertArrayEquals([], goog.json.parse('[]'));
  233. assertArrayEquals([1], goog.json.parse('[1]'));
  234. assertArrayEquals([1, 2], goog.json.parse('[1,2]'));
  235. assertArrayEquals([1, 2, 3], goog.json.parse('[1,2,3]'));
  236. assertArrayEquals([[]], goog.json.parse('[[]]'));
  237. // Note that array-holes are not valid json. However, goog.json.parse
  238. // supports them so that clients can reap the security benefits of
  239. // goog.json.parse even if they are using this non-standard format.
  240. assertArrayEquals([1, /* hole */, 3], goog.json.parse('[1,,3]'));
  241. // make sure we do not get an array for something that looks like an array
  242. assertFalse('{length:0}', 'push' in goog.json.parse('{"length":0}'));
  243. }
  244. function testArrayUnsafeParse() {
  245. function arrayEquals(a1, a2) {
  246. if (a1.length != a2.length) {
  247. return false;
  248. }
  249. for (var i = 0; i < a1.length; i++) {
  250. if (a1[i] != a2[i]) {
  251. return false;
  252. }
  253. }
  254. return true;
  255. }
  256. assertTrue('[]', arrayEquals(goog.json.unsafeParse('[]'), []));
  257. assertTrue('[1]', arrayEquals(goog.json.unsafeParse('[1]'), [1]));
  258. assertTrue('[1,2]', arrayEquals(goog.json.unsafeParse('[1,2]'), [1, 2]));
  259. assertTrue(
  260. '[1,2,3]', arrayEquals(goog.json.unsafeParse('[1,2,3]'), [1, 2, 3]));
  261. assertTrue('[[]]', arrayEquals(goog.json.unsafeParse('[[]]')[0], []));
  262. // make sure we do not get an array for something that looks like an array
  263. assertFalse('{length:0}', 'push' in goog.json.unsafeParse('{"length":0}'));
  264. }
  265. function testObjectParse() {
  266. function objectEquals(a1, a2) {
  267. for (var key in a1) {
  268. if (a1[key] != a2[key]) {
  269. return false;
  270. }
  271. }
  272. return true;
  273. }
  274. assertTrue('{}', objectEquals(goog.json.parse('{}'), {}));
  275. assertTrue('{"a":"b"}', objectEquals(goog.json.parse('{"a":"b"}'), {a: 'b'}));
  276. assertTrue(
  277. '{"a":"b","c":"d"}',
  278. objectEquals(goog.json.parse('{"a":"b","c":"d"}'), {a: 'b', c: 'd'}));
  279. assertTrue(
  280. '{" ":" "}', objectEquals(goog.json.parse('{" ":" "}'), {' ': ' '}));
  281. // make sure we do not get an Object when it is really an array
  282. assertTrue('[0,1]', 'length' in goog.json.parse('[0,1]'));
  283. }
  284. function testObjectUnsafeParse() {
  285. function objectEquals(a1, a2) {
  286. for (var key in a1) {
  287. if (a1[key] != a2[key]) {
  288. return false;
  289. }
  290. }
  291. return true;
  292. }
  293. assertTrue('{}', objectEquals(goog.json.unsafeParse('{}'), {}));
  294. assertTrue(
  295. '{"a":"b"}', objectEquals(goog.json.unsafeParse('{"a":"b"}'), {a: 'b'}));
  296. assertTrue(
  297. '{"a":"b","c":"d"}',
  298. objectEquals(
  299. goog.json.unsafeParse('{"a":"b","c":"d"}'), {a: 'b', c: 'd'}));
  300. assertTrue(
  301. '{" ":" "}',
  302. objectEquals(goog.json.unsafeParse('{" ":" "}'), {' ': ' '}));
  303. // make sure we do not get an Object when it is really an array
  304. assertTrue('[0,1]', 'length' in goog.json.unsafeParse('[0,1]'));
  305. }
  306. function testForValidJson() {
  307. function error_(msg, s) {
  308. assertThrows(
  309. msg + ', Should have raised an exception: ' + s,
  310. goog.partial(goog.json.parse, s));
  311. }
  312. error_('Non closed string', '"dasdas');
  313. error_('undefined is not valid json', 'undefined');
  314. // These numbers cannot be represented in JSON
  315. error_('NaN cannot be presented in JSON', 'NaN');
  316. error_('Infinity cannot be presented in JSON', 'Infinity');
  317. error_('-Infinity cannot be presented in JSON', '-Infinity');
  318. }
  319. function testIsNotValid() {
  320. assertFalse(goog.json.isValid('t'));
  321. assertFalse(goog.json.isValid('r'));
  322. assertFalse(goog.json.isValid('u'));
  323. assertFalse(goog.json.isValid('e'));
  324. assertFalse(goog.json.isValid('f'));
  325. assertFalse(goog.json.isValid('a'));
  326. assertFalse(goog.json.isValid('l'));
  327. assertFalse(goog.json.isValid('s'));
  328. assertFalse(goog.json.isValid('n'));
  329. assertFalse(goog.json.isValid('E'));
  330. assertFalse(goog.json.isValid('+'));
  331. assertFalse(goog.json.isValid('-'));
  332. assertFalse(goog.json.isValid('t++'));
  333. assertFalse(goog.json.isValid('++t'));
  334. assertFalse(goog.json.isValid('t--'));
  335. assertFalse(goog.json.isValid('--t'));
  336. assertFalse(goog.json.isValid('-t'));
  337. assertFalse(goog.json.isValid('+t'));
  338. assertFalse(goog.json.isValid('"\\"')); // "\"
  339. assertFalse(goog.json.isValid('"\\')); // "\
  340. // multiline string using \ at the end is not valid
  341. assertFalse(goog.json.isValid('"a\\\nb"'));
  342. assertFalse(goog.json.isValid('"\n"'));
  343. assertFalse(goog.json.isValid('"\r"'));
  344. assertFalse(goog.json.isValid('"\r\n"'));
  345. // Disallow the unicode newlines
  346. assertFalse(goog.json.isValid('"\u2028"'));
  347. assertFalse(goog.json.isValid('"\u2029"'));
  348. assertFalse(goog.json.isValid(' '));
  349. assertFalse(goog.json.isValid('\n'));
  350. assertFalse(goog.json.isValid('\r'));
  351. assertFalse(goog.json.isValid('\r\n'));
  352. assertFalse(goog.json.isValid('t.r'));
  353. assertFalse(goog.json.isValid('1e'));
  354. assertFalse(goog.json.isValid('1e-'));
  355. assertFalse(goog.json.isValid('1e+'));
  356. assertFalse(goog.json.isValid('1e-'));
  357. assertFalse(goog.json.isValid('"\\\u200D\\"'));
  358. assertFalse(goog.json.isValid('"\\\0\\"'));
  359. assertFalse(goog.json.isValid('"\\\0"'));
  360. assertFalse(goog.json.isValid('"\\0"'));
  361. assertFalse(goog.json.isValid('"\x0c"'));
  362. assertFalse(goog.json.isValid('"\\\u200D\\", alert(\'foo\') //"\n'));
  363. // Disallow referencing variables with names built up from primitives
  364. assertFalse(goog.json.isValid('truefalse'));
  365. assertFalse(goog.json.isValid('null0'));
  366. assertFalse(goog.json.isValid('null0.null0'));
  367. assertFalse(goog.json.isValid('[truefalse]'));
  368. assertFalse(goog.json.isValid('{"a": null0}'));
  369. assertFalse(goog.json.isValid('{"a": null0, "b": 1}'));
  370. }
  371. function testIsValid() {
  372. assertTrue(goog.json.isValid('\n""\n'));
  373. assertTrue(goog.json.isValid('[1\n,2\r,3\u2028\n,4\u2029]'));
  374. assertTrue(goog.json.isValid('"\x7f"'));
  375. assertTrue(goog.json.isValid('"\x09"'));
  376. // Test tab characters in json.
  377. assertTrue(goog.json.isValid('{"\t":"\t"}'));
  378. }
  379. function testDoNotSerializeProto() {
  380. function F(){};
  381. F.prototype = {c: 3};
  382. var obj = new F;
  383. obj.a = 1;
  384. obj.b = 2;
  385. assertEquals(
  386. 'Should not follow the prototype chain', '{"a":1,"b":2}',
  387. goog.json.serialize(obj));
  388. }
  389. function testEscape() {
  390. var unescaped = '1a*/]';
  391. assertEquals(
  392. 'Should not escape', '"' + unescaped + '"',
  393. goog.json.serialize(unescaped));
  394. var escaped = '\n\x7f\u1049';
  395. assertEquals(
  396. 'Should escape', '',
  397. findCommonChar(escaped, goog.json.serialize(escaped)));
  398. assertEquals(
  399. 'Should eval to the same string after escaping', escaped,
  400. goog.json.parse(goog.json.serialize(escaped)));
  401. }
  402. function testReplacer() {
  403. assertSerialize('[null,null,0]', [, , 0]);
  404. assertSerialize('[0,0,{"x":0}]', [, , {x: 0}], function(k, v) {
  405. if (v === undefined && goog.isArray(this)) {
  406. return 0;
  407. }
  408. return v;
  409. });
  410. assertSerialize('[0,1,2,3]', [0, 0, 0, 0], function(k, v) {
  411. var kNum = Number(k);
  412. if (k && !isNaN(kNum)) {
  413. return kNum;
  414. }
  415. return v;
  416. });
  417. var f = function(k, v) { return typeof v == 'number' ? v + 1 : v; };
  418. assertSerialize('{"a":1,"b":{"c":2}}', {'a': 0, 'b': {'c': 1}}, f);
  419. }
  420. function testDateSerialize() {
  421. assertSerialize('{}', new Date(0));
  422. }
  423. function testToJSONSerialize() {
  424. assertSerialize('{}', {toJSON: goog.functions.constant('serialized')});
  425. assertSerialize('{"toJSON":"normal"}', {toJSON: 'normal'});
  426. }
  427. function testTryNativeJson() {
  428. goog.json.TRY_NATIVE_JSON = true;
  429. var error;
  430. goog.json.setErrorLogger(function(message, ex) {
  431. error = message;
  432. });
  433. goog.json.unsafeParse('{"a":[,1]}');
  434. assertEquals('Invalid JSON: {"a":[,1]}', error);
  435. error = undefined;
  436. goog.json.parse('{"a":[,1]}');
  437. assertEquals('Invalid JSON: {"a":[,1]}', error);
  438. goog.json.TRY_NATIVE_JSON = false;
  439. goog.json.setErrorLogger(goog.nullFunction);
  440. }
  441. /**
  442. * Asserts that the given object serializes to the given value.
  443. * If the current browser has an implementation of JSON.serialize,
  444. * we make sure our version matches that one.
  445. */
  446. function assertSerialize(expected, obj, opt_replacer) {
  447. assertEquals(expected, goog.json.serialize(obj, opt_replacer));
  448. // goog.json.serialize escapes non-ASCI characters while JSON.stringify
  449. // doesn’t. This is expected so do not compare the results.
  450. if (typeof obj == 'string' && obj.charCodeAt(0) > 0x7f) return;
  451. // I'm pretty sure that the goog.json.serialize behavior is correct by the ES5
  452. // spec, but JSON.stringify(undefined) is undefined on all browsers.
  453. if (obj === undefined) return;
  454. // Browsers don't serialize undefined properties, but goog.json.serialize does
  455. if (goog.isObject(obj) && ('a' in obj) && obj['a'] === undefined) return;
  456. // Replacers are broken on IE and older versions of firefox.
  457. if (opt_replacer && !goog.userAgent.WEBKIT) return;
  458. // goog.json.serialize does not stringify dates the same way.
  459. if (obj instanceof Date) return;
  460. // goog.json.serialize does not stringify functions the same way.
  461. if (obj instanceof Function) return;
  462. // goog.json.serialize doesn't use the toJSON method.
  463. if (goog.isObject(obj) && goog.isFunction(obj.toJSON)) return;
  464. if (typeof JSON != 'undefined') {
  465. assertEquals(
  466. 'goog.json.serialize does not match JSON.stringify', expected,
  467. JSON.stringify(obj, opt_replacer));
  468. }
  469. }
  470. /**
  471. * @param {string} a
  472. * @param {string} b
  473. * @return {string} any common character between two strings a and b.
  474. */
  475. function findCommonChar(a, b) {
  476. for (var i = 0; i < b.length; i++) {
  477. if (a.indexOf(b.charAt(i)) >= 0) {
  478. return b.charAt(i);
  479. }
  480. }
  481. return '';
  482. }