tests.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. (function(root) {
  2. 'use strict';
  3. var noop = Function.prototype;
  4. var load = (typeof require == 'function' && !(root.define && define.amd)) ?
  5. require :
  6. (!root.document && root.java && root.load) || noop;
  7. var QUnit = (function() {
  8. return root.QUnit || (
  9. root.addEventListener || (root.addEventListener = noop),
  10. root.setTimeout || (root.setTimeout = noop),
  11. root.QUnit = load('../node_modules/qunitjs/qunit/qunit.js') || root.QUnit,
  12. addEventListener === noop && delete root.addEventListener,
  13. root.QUnit
  14. );
  15. }());
  16. var qe = load('../node_modules/qunit-extras/qunit-extras.js');
  17. if (qe) {
  18. qe.runInContext(root);
  19. }
  20. /** The `punycode` object to test */
  21. var punycode = root.punycode || (root.punycode = (
  22. punycode = load('../punycode.js') || root.punycode,
  23. punycode = punycode.punycode || punycode
  24. ));
  25. // Quick and dirty test to see if we’re in Node or PhantomJS
  26. var runExtendedTests = (function() {
  27. try {
  28. return process.argv[0] == 'node' || root.phantom;
  29. } catch (exception) { }
  30. }());
  31. /** Data that will be used in the tests */
  32. var allSymbols = runExtendedTests && require('./data.js');
  33. var testData = {
  34. 'strings': [
  35. {
  36. 'description': 'a single basic code point',
  37. 'decoded': 'Bach',
  38. 'encoded': 'Bach-'
  39. },
  40. {
  41. 'description': 'a single non-ASCII character',
  42. 'decoded': '\xFC',
  43. 'encoded': 'tda'
  44. },
  45. {
  46. 'description': 'multiple non-ASCII characters',
  47. 'decoded': '\xFC\xEB\xE4\xF6\u2665',
  48. 'encoded': '4can8av2009b'
  49. },
  50. {
  51. 'description': 'mix of ASCII and non-ASCII characters',
  52. 'decoded': 'b\xFCcher',
  53. 'encoded': 'bcher-kva'
  54. },
  55. {
  56. 'description': 'long string with both ASCII and non-ASCII characters',
  57. 'decoded': 'Willst du die Bl\xFCthe des fr\xFChen, die Fr\xFCchte des sp\xE4teren Jahres',
  58. 'encoded': 'Willst du die Blthe des frhen, die Frchte des spteren Jahres-x9e96lkal'
  59. },
  60. // http://tools.ietf.org/html/rfc3492#section-7.1
  61. {
  62. 'description': 'Arabic (Egyptian)',
  63. 'decoded': '\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644\u0645\u0648\u0634\u0639\u0631\u0628\u064A\u061F',
  64. 'encoded': 'egbpdaj6bu4bxfgehfvwxn'
  65. },
  66. {
  67. 'description': 'Chinese (simplified)',
  68. 'decoded': '\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2d\u6587',
  69. 'encoded': 'ihqwcrb4cv8a8dqg056pqjye'
  70. },
  71. {
  72. 'description': 'Chinese (traditional)',
  73. 'decoded': '\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587',
  74. 'encoded': 'ihqwctvzc91f659drss3x8bo0yb'
  75. },
  76. {
  77. 'description': 'Czech',
  78. 'decoded': 'Pro\u010Dprost\u011Bnemluv\xED\u010Desky',
  79. 'encoded': 'Proprostnemluvesky-uyb24dma41a'
  80. },
  81. {
  82. 'description': 'Hebrew',
  83. 'decoded': '\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8\u05DC\u05D0\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2\u05D1\u05E8\u05D9\u05EA',
  84. 'encoded': '4dbcagdahymbxekheh6e0a7fei0b'
  85. },
  86. {
  87. 'description': 'Hindi (Devanagari)',
  88. 'decoded': '\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D\u0926\u0940\u0915\u094D\u092F\u094B\u0902\u0928\u0939\u0940\u0902\u092C\u094B\u0932\u0938\u0915\u0924\u0947\u0939\u0948\u0902',
  89. 'encoded': 'i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd'
  90. },
  91. {
  92. 'description': 'Japanese (kanji and hiragana)',
  93. 'decoded': '\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092\u8A71\u3057\u3066\u304F\u308C\u306A\u3044\u306E\u304B',
  94. 'encoded': 'n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa'
  95. },
  96. {
  97. 'description': 'Korean (Hangul syllables)',
  98. 'decoded': '\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774\uD55C\uAD6D\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74\uC5BC\uB9C8\uB098\uC88B\uC744\uAE4C',
  99. 'encoded': '989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879ccm6fea98c'
  100. },
  101. /**
  102. * As there's no way to do it in JavaScript, Punycode.js doesn't support
  103. * mixed-case annotation (which is entirely optional as per the RFC).
  104. * So, while the RFC sample string encodes to:
  105. * `b1abfaaepdrnnbgefbaDotcwatmq2g4l`
  106. * Without mixed-case annotation it has to encode to:
  107. * `b1abfaaepdrnnbgefbadotcwatmq2g4l`
  108. * https://github.com/bestiejs/punycode.js/issues/3
  109. */
  110. {
  111. 'description': 'Russian (Cyrillic)',
  112. 'decoded': '\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E\u043D\u0438\u043D\u0435\u0433\u043E\u0432\u043E\u0440\u044F\u0442\u043F\u043E\u0440\u0443\u0441\u0441\u043A\u0438',
  113. 'encoded': 'b1abfaaepdrnnbgefbadotcwatmq2g4l'
  114. },
  115. {
  116. 'description': 'Spanish',
  117. 'decoded': 'Porqu\xE9nopuedensimplementehablarenEspa\xF1ol',
  118. 'encoded': 'PorqunopuedensimplementehablarenEspaol-fmd56a'
  119. },
  120. {
  121. 'description': 'Vietnamese',
  122. 'decoded': 'T\u1EA1isaoh\u1ECDkh\xF4ngth\u1EC3ch\u1EC9n\xF3iti\u1EBFngVi\u1EC7t',
  123. 'encoded': 'TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g'
  124. },
  125. {
  126. 'decoded': '3\u5E74B\u7D44\u91D1\u516B\u5148\u751F',
  127. 'encoded': '3B-ww4c5e180e575a65lsy2b'
  128. },
  129. {
  130. 'decoded': '\u5B89\u5BA4\u5948\u7F8E\u6075-with-SUPER-MONKEYS',
  131. 'encoded': '-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n'
  132. },
  133. {
  134. 'decoded': 'Hello-Another-Way-\u305D\u308C\u305E\u308C\u306E\u5834\u6240',
  135. 'encoded': 'Hello-Another-Way--fc4qua05auwb3674vfr0b'
  136. },
  137. {
  138. 'decoded': '\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B2',
  139. 'encoded': '2-u9tlzr9756bt3uc0v'
  140. },
  141. {
  142. 'decoded': 'Maji\u3067Koi\u3059\u308B5\u79D2\u524D',
  143. 'encoded': 'MajiKoi5-783gue6qz075azm5e'
  144. },
  145. {
  146. 'decoded': '\u30D1\u30D5\u30A3\u30FCde\u30EB\u30F3\u30D0',
  147. 'encoded': 'de-jg4avhby1noc0d'
  148. },
  149. {
  150. 'decoded': '\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067',
  151. 'encoded': 'd9juau41awczczp'
  152. },
  153. /**
  154. * This example is an ASCII string that breaks the existing rules for host
  155. * name labels. (It's not a realistic example for IDNA, because IDNA never
  156. * encodes pure ASCII labels.)
  157. */
  158. {
  159. 'description': 'ASCII string that breaks the existing rules for host-name labels',
  160. 'decoded': '-> $1.00 <-',
  161. 'encoded': '-> $1.00 <--'
  162. }
  163. ],
  164. 'ucs2': [
  165. // Every Unicode symbol is tested separately. These are just the extra
  166. // tests for symbol combinations:
  167. {
  168. 'description': 'Consecutive astral symbols',
  169. 'decoded': [127829, 119808, 119558, 119638],
  170. 'encoded': '\uD83C\uDF55\uD835\uDC00\uD834\uDF06\uD834\uDF56'
  171. },
  172. {
  173. 'description': 'U+D800 (high surrogate) followed by non-surrogates',
  174. 'decoded': [55296, 97, 98],
  175. 'encoded': '\uD800ab'
  176. },
  177. {
  178. 'description': 'U+DC00 (low surrogate) followed by non-surrogates',
  179. 'decoded': [56320, 97, 98],
  180. 'encoded': '\uDC00ab'
  181. },
  182. {
  183. 'description': 'High surrogate followed by another high surrogate',
  184. 'decoded': [0xD800, 0xD800],
  185. 'encoded': '\uD800\uD800'
  186. },
  187. {
  188. 'description': 'Unmatched high surrogate, followed by a surrogate pair, followed by an unmatched high surrogate',
  189. 'decoded': [0xD800, 0x1D306, 0xD800],
  190. 'encoded': '\uD800\uD834\uDF06\uD800'
  191. },
  192. {
  193. 'description': 'Low surrogate followed by another low surrogate',
  194. 'decoded': [0xDC00, 0xDC00],
  195. 'encoded': '\uDC00\uDC00'
  196. },
  197. {
  198. 'description': 'Unmatched low surrogate, followed by a surrogate pair, followed by an unmatched low surrogate',
  199. 'decoded': [0xDC00, 0x1D306, 0xDC00],
  200. 'encoded': '\uDC00\uD834\uDF06\uDC00'
  201. }
  202. ],
  203. 'domains': [
  204. {
  205. 'decoded': 'ma\xF1ana.com',
  206. 'encoded': 'xn--maana-pta.com'
  207. },
  208. { // https://github.com/bestiejs/punycode.js/issues/17
  209. 'decoded': 'example.com.',
  210. 'encoded': 'example.com.'
  211. },
  212. {
  213. 'decoded': 'b\xFCcher.com',
  214. 'encoded': 'xn--bcher-kva.com'
  215. },
  216. {
  217. 'decoded': 'caf\xE9.com',
  218. 'encoded': 'xn--caf-dma.com'
  219. },
  220. {
  221. 'decoded': '\u2603-\u2318.com',
  222. 'encoded': 'xn----dqo34k.com'
  223. },
  224. {
  225. 'decoded': '\uD400\u2603-\u2318.com',
  226. 'encoded': 'xn----dqo34kn65z.com'
  227. },
  228. {
  229. 'description': 'Emoji',
  230. 'decoded': '\uD83D\uDCA9.la',
  231. 'encoded': 'xn--ls8h.la'
  232. },
  233. {
  234. 'description': 'Email address',
  235. 'decoded': '\u0434\u0436\u0443\u043C\u043B\u0430@\u0434\u0436p\u0443\u043C\u043B\u0430\u0442\u0435\u0441\u0442.b\u0440\u0444a',
  236. 'encoded': '\u0434\u0436\u0443\u043C\u043B\u0430@xn--p-8sbkgc5ag7bhce.xn--ba-lmcq'
  237. }
  238. ],
  239. 'separators': [
  240. {
  241. 'description': 'Using U+002E as separator',
  242. 'decoded': 'ma\xF1ana\x2Ecom',
  243. 'encoded': 'xn--maana-pta.com'
  244. },
  245. {
  246. 'description': 'Using U+3002 as separator',
  247. 'decoded': 'ma\xF1ana\u3002com',
  248. 'encoded': 'xn--maana-pta.com'
  249. },
  250. {
  251. 'description': 'Using U+FF0E as separator',
  252. 'decoded': 'ma\xF1ana\uFF0Ecom',
  253. 'encoded': 'xn--maana-pta.com'
  254. },
  255. {
  256. 'description': 'Using U+FF61 as separator',
  257. 'decoded': 'ma\xF1ana\uFF61com',
  258. 'encoded': 'xn--maana-pta.com'
  259. }
  260. ]
  261. };
  262. /*--------------------------------------------------------------------------*/
  263. // simple `Array#forEach`-like helper function
  264. function each(array, fn) {
  265. var index = array.length;
  266. while (index--) {
  267. fn(array[index], index);
  268. }
  269. }
  270. // `throws` is a reserved word in ES3; alias it to avoid errors
  271. var raises = QUnit.assert['throws'];
  272. /*--------------------------------------------------------------------------*/
  273. // Explicitly call `QUnit.module()` instead of `module()` in case we are in
  274. // a CLI environment.
  275. QUnit.module('punycode');
  276. test('punycode.ucs2.decode', function() {
  277. // Test all Unicode code points separately.
  278. runExtendedTests && each(allSymbols, function(string, codePoint) {
  279. deepEqual(punycode.ucs2.decode(string), [codePoint], 'Decoding symbol with code point ' + codePoint);
  280. });
  281. each(testData.ucs2, function(object) {
  282. deepEqual(punycode.ucs2.decode(object.encoded), object.decoded, object.description);
  283. });
  284. raises(
  285. function() {
  286. punycode.decode('\x81-');
  287. },
  288. RangeError,
  289. 'RangeError: Illegal input >= 0x80 (not a basic code point)'
  290. );
  291. raises(
  292. function() {
  293. punycode.decode('\x81');
  294. },
  295. RangeError,
  296. 'RangeError: Overflow: input needs wider integers to process'
  297. );
  298. });
  299. test('punycode.ucs2.encode', function() {
  300. // test all Unicode code points separately
  301. runExtendedTests && each(allSymbols, function(string, codePoint) {
  302. deepEqual(punycode.ucs2.encode([codePoint]), string, 'Encoding code point ' + codePoint);
  303. });
  304. each(testData.ucs2, function(object) {
  305. equal(punycode.ucs2.encode(object.decoded), object.encoded, object.description);
  306. });
  307. var codePoints = [0x61, 0x62, 0x63];
  308. var result = punycode.ucs2.encode(codePoints);
  309. equal(result, 'abc');
  310. deepEqual(codePoints, [0x61, 0x62, 0x63], 'Do not mutate argument array');
  311. });
  312. test('punycode.decode', function() {
  313. each(testData.strings, function(object) {
  314. equal(punycode.decode(object.encoded), object.decoded, object.description);
  315. });
  316. equal(punycode.decode('ZZZ'), '\u7BA5', 'Uppercase Z');
  317. });
  318. test('punycode.encode', function() {
  319. each(testData.strings, function(object) {
  320. equal(punycode.encode(object.decoded), object.encoded, object.description);
  321. });
  322. });
  323. test('punycode.toUnicode', function() {
  324. each(testData.domains, function(object) {
  325. equal(punycode.toUnicode(object.encoded), object.decoded, object.description);
  326. });
  327. /**
  328. * Domain names (or other strings) that don't start with `xn--` may not be
  329. * converted.
  330. */
  331. each(testData.strings, function(object) {
  332. var message = 'Domain names (or other strings) that don\'t start with `xn--` may not be converted';
  333. equal(punycode.toUnicode(object.encoded), object.encoded, message);
  334. equal(punycode.toUnicode(object.decoded), object.decoded, message);
  335. });
  336. });
  337. test('punycode.toASCII', function() {
  338. each(testData.domains, function(object) {
  339. equal(punycode.toASCII(object.decoded), object.encoded, object.description);
  340. });
  341. /**
  342. * Domain names (or other strings) that are already in ASCII may not be
  343. * converted.
  344. */
  345. each(testData.strings, function(object) {
  346. equal(punycode.toASCII(object.encoded), object.encoded, 'Domain names (or other strings) that are already in ASCII may not be converted');
  347. });
  348. /**
  349. * IDNA2003 separators must be supported for backwards compatibility.
  350. */
  351. each(testData.separators, function(object) {
  352. var message = 'IDNA2003 separators must be supported for backwards compatibility';
  353. equal(punycode.toASCII(object.decoded), object.encoded, message);
  354. });
  355. });
  356. /*--------------------------------------------------------------------------*/
  357. // configure QUnit and call `QUnit.start()` for
  358. // Narwhal, Node.js, PhantomJS, Rhino, and RingoJS
  359. if (!root.document || root.phantom) {
  360. QUnit.config.noglobals = true;
  361. QUnit.start();
  362. }
  363. }(typeof global == 'object' && global || this));