function shouldBe(actual, expected) { if (actual !== expected) throw new Error("bad value: " + String(actual)); } function shouldThrow(func, message) { var error = null; try { func(); } catch (e) { error = e; } if (!error) throw new Error("not thrown."); if (String(error) !== message) throw new Error("bad error: " + String(error)); } function toCodePoints(string) { var result = []; for (var codePoint of string) { result.push(codePoint.codePointAt(0)); } return result; } shouldBe(String.fromCodePoint(), ""); shouldBe(String.fromCodePoint(0), "\0"); shouldBe(String.fromCodePoint(0, 0), "\0\0"); var tests = [ "", "Hello", "Cocoa", "Cappuccino Cocoa", "日本語", "マルチバイト", "吉野屋", "𠮷野家", // Contain a surrogate pair. ]; for (var test of tests) { shouldBe(String.fromCodePoint.apply(String, toCodePoints(test)), test); } function passThrough(codePoint) { var string = String.fromCodePoint(codePoint); shouldBe(string.codePointAt(0), codePoint); } var numberTests = [ [ 0x10FFFF, "\uDBFF\uDFFF" ], [ 0x10FFFE, "\uDBFF\uDFFE" ], [ 0xFFFF, "\uFFFF" ], [ 0x10000, "\uD800\uDC00" ], [ 0x10001, "\uD800\uDC01" ], [ -0.0, "\u0000" ], [ 0xD800, "\uD800" ], [ 0xDC00, "\uDC00" ], ]; for (var test of numberTests) { shouldBe(String.fromCodePoint(test[0]), test[1]); } shouldBe(String.fromCodePoint(0xD800, 0xDC00).codePointAt(0), 0x10000); // Non-character code points. for (var i = 0; i < 17; ++i) { var plane = 0x10000 * i; passThrough(plane + 0xFFFE); passThrough(plane + 0xFFFF); } for (var start = 0xFDD0; start <= 0xFDEF; ++start) { passThrough(start); } var invalidTests = [ -1, 1.2, 1.5, 30.01, -11.0, NaN, Number.Infinity, -Number.Infinity, 0x10FFFF + 1, 0x7FFFFFFF, 0x7FFFFFFF + 1, 0xFFFFFFFF, 0xFFFFFFFF + 1, 0x100000000 + 32, // String.fromCharCode(0x100000000 + 32) produces a space, but String.fromCodePoint should throw an error. "Hello", undefined, {}, ]; for (var test of invalidTests) { shouldThrow(function () { String.fromCodePoint(test); }, "RangeError: Arguments contain a value that is out of range of code points"); } // toNumber causes errors. shouldThrow(function () { String.fromCodePoint(Symbol.iterator); }, "TypeError: Cannot convert a symbol to a number") var toNumberObject = { valueOf() { throw new Error("valueOf is called"); } }; shouldThrow(function () { String.fromCodePoint(toNumberObject); }, "Error: valueOf is called") shouldThrow(function () { String.fromCodePoint(Symbol.iterator, toNumberObject); }, "TypeError: Cannot convert a symbol to a number") var convertAndPassTests = [ [ null, "\0" ], [ [], "\0" ], [ "0x41", "A" ], [ "", "\0" ], [ true, "\u0001" ], [ false, "\u0000" ], ]; for (var test of convertAndPassTests) { shouldBe(String.fromCodePoint(test[0]), test[1]); }