tests.js 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. // In Node.JS, `module` is a predefined object, which makes the QUnit `module` definitions fail
  2. // unless we redefine it.
  3. module = QUnit.module;
  4. // When using node-qunit on the command line, the module is imported as is and we need to point at
  5. // the XRegExp class inside the module. This does nothing in the browser, where XRegExp is already
  6. // loaded in the global scope.
  7. if (typeof XRegExp === "undefined" && typeof xregexp !== "undefined") {
  8. var XRegExp = xregexp.XRegExp;
  9. }
  10. //-------------------------------------------------------------------
  11. module("API");
  12. //-------------------------------------------------------------------
  13. test("Basic availability", function () {
  14. ok(XRegExp, "XRegExp exists");
  15. ok(XRegExp.addToken, "XRegExp.addToken exists");
  16. ok(XRegExp.cache, "XRegExp.cache exists");
  17. ok(XRegExp.escape, "XRegExp.escape exists");
  18. ok(XRegExp.exec, "XRegExp.exec exists");
  19. ok(XRegExp.forEach, "XRegExp.forEach exists");
  20. ok(XRegExp.globalize, "XRegExp.globalize exists");
  21. ok(XRegExp.install, "XRegExp.install exists");
  22. ok(XRegExp.isInstalled, "XRegExp.isInstalled exists");
  23. ok(XRegExp.isRegExp, "XRegExp.isRegExp exists");
  24. ok(XRegExp.matchChain, "XRegExp.matchChain exists");
  25. ok(XRegExp.replace, "XRegExp.replace exists");
  26. ok(XRegExp.split, "XRegExp.split exists");
  27. ok(XRegExp.test, "XRegExp.test exists");
  28. ok(XRegExp.uninstall, "XRegExp.uninstall exists");
  29. ok(XRegExp.union, "XRegExp.union exists");
  30. ok(XRegExp.version, "XRegExp.version exists");
  31. });
  32. test("XRegExp", function () {
  33. var blankRegex = XRegExp("(?:)"),
  34. regexGIM = XRegExp("(?:)", "gim");
  35. equal(XRegExp("").source, new RegExp("").source, "Empty regex source (test 1)");
  36. equal(XRegExp("(?:)").source, /(?:)/.source, "Empty regex source (test 2)");
  37. equal(XRegExp().source, new RegExp().source, "undefined regex source");
  38. equal(XRegExp(null).source, new RegExp(null).source, "null regex source");
  39. equal(XRegExp(NaN).source, new RegExp(NaN).source, "NaN regex source");
  40. equal(XRegExp(1).source, new RegExp(1).source, "numeric regex source");
  41. equal(XRegExp({}).source, new RegExp({}).source, "object regex source");
  42. equal(XRegExp("").global, false, "Regex without flags is not global");
  43. ok(XRegExp("", "g").global, "Regex with global flag is global");
  44. ok(XRegExp("", "i").ignoreCase, "Regex with ignoreCase flag is ignoreCase");
  45. ok(XRegExp("", "m").multiline, "Regex with multiline flag is multiline");
  46. ok(regexGIM.global && regexGIM.ignoreCase && regexGIM.multiline, "Regex with flags gim is global, ignoreCase, multiline");
  47. deepEqual(blankRegex, XRegExp(blankRegex), "Regex copy and original are alike");
  48. notEqual(blankRegex, XRegExp(blankRegex), "Regex copy is new instance");
  49. ok(XRegExp("").xregexp, "XRegExp has xregexp property");
  50. notStrictEqual(XRegExp("").xregexp.captureNames, undefined, "XRegExp has captureNames property");
  51. equal(XRegExp("").xregexp.captureNames, null, "Empty XRegExp has null captureNames");
  52. notStrictEqual(XRegExp("").xregexp.isNative, undefined, "XRegExp has isNative property");
  53. equal(XRegExp("").xregexp.isNative, false, "XRegExp has isNative false");
  54. equal(XRegExp(XRegExp("")).xregexp.isNative, false, "Copied XRegExp has isNative false");
  55. equal(XRegExp(new RegExp("")).xregexp.isNative, true, "Copied RegExp has isNative true");
  56. equal(XRegExp.exec("aa", XRegExp(XRegExp("(?<name>a)\\k<name>"))).name, "a", "Copied XRegExp retains named capture properties");
  57. raises(function () {XRegExp(/(?:)/, "g");}, Error, "Regex copy with flag throws");
  58. ok(XRegExp("") instanceof RegExp, "XRegExp object is instanceof RegExp");
  59. equal(XRegExp("").constructor, RegExp, "XRegExp object constructor is RegExp");
  60. raises(function () {XRegExp("", "gg");}, SyntaxError, "Regex with duplicate native flags throws");
  61. raises(function () {XRegExp("", "ss");}, SyntaxError, "Regex with duplicate nonnative flags throws (test 1)");
  62. raises(function () {XRegExp("", "sis");}, SyntaxError, "Regex with duplicate nonnative flags throws (test 2)");
  63. raises(function () {XRegExp("", "?");}, SyntaxError, "Unsupported flag throws");
  64. ok(!XRegExp("(?:)", "x").extended, "Nonnative flag x does not set extended property");
  65. });
  66. test("XRegExp.addToken", function () {
  67. XRegExp.install("extensibility");
  68. XRegExp.addToken(/\x01/, function () {return "1";});
  69. XRegExp.addToken(/\x02/, function () {return "2";}, {scope: "class"});
  70. XRegExp.addToken(/\x03/, function () {return "3";}, {scope: "default"});
  71. XRegExp.addToken(/\x04/, function () {return "4";}, {scope: "all"});
  72. XRegExp.addToken(/\x05/, function () {return "5";}, {
  73. scope: "default",
  74. trigger: function () {return this.hasFlag("5");},
  75. customFlags: "5"
  76. });
  77. XRegExp.uninstall("extensibility");
  78. ok(XRegExp("\x01").test("1"), "Default scope matches outside class");
  79. ok(!XRegExp("[\x01]").test("1"), "Default scope doesn't match inside class");
  80. ok(!XRegExp("\x02").test("2"), "Explicit class scope doesn't match outside class");
  81. ok(XRegExp("[\x02]").test("2"), "Explicit class scope matches inside class");
  82. ok(XRegExp("\x03").test("3"), "Explicit default scope matches outside class");
  83. ok(!XRegExp("[\x03]").test("3"), "Explicit default scope doesn't match inside class");
  84. ok(XRegExp("\x04").test("4"), "Explicit all scope matches outside class");
  85. ok(XRegExp("[\x04]").test("4"), "Explicit all scope matches inside class");
  86. ok(!XRegExp("\x05").test("5"), "Trigger with hasFlag skips token when flag is missing");
  87. ok(XRegExp("\x05", "5").test("5"), "Trigger with hasFlag uses token when flag is included");
  88. });
  89. test("XRegExp.cache", function () {
  90. var cached1 = XRegExp.cache("(?:)");
  91. var cached2 = XRegExp.cache("(?:)");
  92. var regexWithFlags = XRegExp(". +()\\1 1", "gimsx");
  93. ok(cached1 instanceof RegExp, "Returns RegExp");
  94. strictEqual(cached1, cached2, "References to separately cached patterns refer to same object");
  95. deepEqual(XRegExp.cache(". +()\\1 1", "gimsx"), regexWithFlags, "Cached pattern plus flags");
  96. });
  97. test("XRegExp.escape", function () {
  98. equal(XRegExp.escape("[()*+?.\\^$|"), "\\[\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|", "Metacharacters are escaped");
  99. equal(XRegExp.escape("]{}-, #"), "\\]\\{\\}\\-\\,\\ \\#", "Occasional metacharacters are escaped");
  100. equal(XRegExp.escape("abc_<123>!"), "abc_<123>!", "Nonmetacharacters are not escaped");
  101. });
  102. test("XRegExp.exec", function () {
  103. var rX = /x/g;
  104. var rA = /a/g;
  105. var xregexp = XRegExp("(?<name>a)"); // tests expect this to be nonglobal and use named capture
  106. var str = "abcxdef";
  107. var match;
  108. ok(XRegExp.exec(str, rX, 2), "Pos test 1");
  109. ok(!XRegExp.exec(str, rX, 5), "Pos test 2");
  110. rX.lastIndex = 5;
  111. ok(XRegExp.exec(str, rX, 2), "Pos ignores lastIndex test 1");
  112. rX.lastIndex = 0;
  113. ok(!XRegExp.exec(str, rX, 5), "Pos ignores lastIndex test 2");
  114. rA.lastIndex = 5;
  115. ok(XRegExp.exec(str, rA), "Pos ignores lastIndex test 3 (pos defaults to 0)");
  116. ok(XRegExp.exec(str, rX, 0), "Undefined sticky allows matching after pos");
  117. ok(XRegExp.exec(str, rX, 0, false), "Explicit sticky=false allows matching after pos");
  118. ok(!XRegExp.exec(str, rX, 0, true), "Sticky match fails if match possible after (but not at) pos");
  119. ok(!XRegExp.exec(str, rX, 0, "sticky"), "String 'sticky' triggers sticky mode");
  120. ok(XRegExp.exec(str, rX, 3, true), "Sticky match succeeds if match at pos");
  121. equal(XRegExp.exec(str, rX, 5), null, "Result of failure is null");
  122. deepEqual(XRegExp.exec(str, xregexp), ["a", "a"], "Result of successful match is array with backreferences");
  123. match = XRegExp.exec(str, xregexp);
  124. equal(match.name, "a", "Match result includes named capture properties");
  125. xregexp.lastIndex = 5;
  126. XRegExp.exec(str, xregexp);
  127. equal(xregexp.lastIndex, 5, "lastIndex of nonglobal regex left as is");
  128. rX.lastIndex = 0;
  129. XRegExp.exec(str, rX);
  130. equal(rX.lastIndex, 4, "lastIndex of global regex updated to end of match");
  131. rX.lastIndex = 5;
  132. XRegExp.exec(str, rX, 2, true);
  133. equal(rX.lastIndex, 0, "lastIndex of global regex updated to 0 after failure");
  134. equal(XRegExp.exec("abc", /x/, 5), null, "pos greater than string length results in failure");
  135. if (RegExp.prototype.sticky !== undefined) {
  136. var stickyRegex = new RegExp("x", "y"); // can't use /x/y even behind `if` because it errors during compilation in IE9
  137. ok(XRegExp.exec(str, stickyRegex, 0, false), "Explicit sticky=false overrides flag y");
  138. ok(!XRegExp.exec(str, stickyRegex, 0), "Sticky follows flag y when not explicitly specified");
  139. }
  140. });
  141. test("XRegExp.forEach", function () {
  142. var str = "abc 123 def";
  143. var regex = XRegExp("(?<first>\\w)\\w*");
  144. var regexG = XRegExp("(?<first>\\w)\\w*", "g");
  145. deepEqual(XRegExp.forEach(str, regex, function (m) {this.push(m[0]);}, []), ["abc", "123", "def"], "Match strings with nonglobal regex");
  146. deepEqual(XRegExp.forEach(str, regexG, function (m) {this.push(m[0]);}, []), ["abc", "123", "def"], "Match strings with global regex");
  147. deepEqual(XRegExp.forEach(str, regex, function (m) {this.push(m.first);}, []), ["a", "1", "d"], "Named backreferences");
  148. deepEqual(XRegExp.forEach(str, regex, function (m) {this.push(m.index);}, []), [0, 4, 8], "Match indexes");
  149. deepEqual(XRegExp.forEach(str, regex, function (m, i) {this.push(i);}, []), [0, 1, 2], "Match numbers");
  150. deepEqual(XRegExp.forEach(str, regex, function (m, i, s) {this.push(s);}, []), [str, str, str], "Source strings");
  151. deepEqual(XRegExp.forEach(str, regex, function (m, i, s, r) {this.push(r);}, []), [regex, regex, regex], "Source regexes");
  152. var str2 = str;
  153. deepEqual(XRegExp.forEach(str2, regex, function (m, i, s) {this.push(s); s += s; str2 += str2;}, []), [str, str, str], "Source string manipulation in callback doesn't affect iteration");
  154. var regex2 = XRegExp(regex);
  155. deepEqual(XRegExp.forEach(str, regex2, function (m, i, s, r) {this.push(i); r = /x/; regex2 = /x/;}, []), [0, 1, 2], "Source regex manipulation in callback doesn't affect iteration");
  156. regexG.lastIndex = 4;
  157. deepEqual(XRegExp.forEach(str, regexG, function (m) {this.push(m[0]);}, []), ["abc", "123", "def"], "Iteration starts at pos 0, ignoring lastIndex");
  158. regex.lastIndex = 4;
  159. XRegExp.forEach(str, regex, function () {});
  160. equal(regex.lastIndex, 4, "lastIndex of nonglobal regex unmodified after iteration");
  161. regexG.lastIndex = 4;
  162. XRegExp.forEach(str, regexG, function () {});
  163. equal(regexG.lastIndex, 0, "lastIndex of global regex reset to 0 after iteration");
  164. var rgOrig = /\d+/g, interimLastIndex1 = 0, interimLastIndex2 = 0;
  165. XRegExp.forEach(str, rgOrig, function (m, i, s, r) {
  166. interimLastIndex1 = rgOrig.lastIndex;
  167. interimLastIndex2 = r.lastIndex;
  168. });
  169. equal(interimLastIndex1, 7, "Global regex lastIndex updated during iterations (test 1)");
  170. equal(interimLastIndex2, 7, "Global regex lastIndex updated during iterations (test 2)");
  171. var rOrig = /\d+/, interimLastIndex1 = 0, interimLastIndex2 = 0;
  172. XRegExp.forEach(str, rOrig, function (m, i, s, r) {
  173. interimLastIndex1 = rOrig.lastIndex;
  174. interimLastIndex2 = r.lastIndex;
  175. });
  176. equal(interimLastIndex1, 0, "Nonglobal regex lastIndex not updated during iterations (test 1)");
  177. equal(interimLastIndex2, 0, "Nonglobal regex lastIndex not updated during iterations (test 2)");
  178. });
  179. test("XRegExp.globalize", function () {
  180. var hasNativeY = typeof RegExp.prototype.sticky !== "undefined";
  181. var regex = XRegExp("(?<name>a)\\k<name>", "im" + (hasNativeY ? "y" : ""));
  182. var globalCopy = XRegExp.globalize(regex);
  183. var globalOrig = XRegExp("(?:)", "g");
  184. notEqual(regex, globalCopy, "Copy is new instance");
  185. ok(globalCopy.global, "Copy is global");
  186. equal(regex.source, globalCopy.source, "Copy has same source");
  187. ok(regex.ignoreCase === globalCopy.ignoreCase && regex.multiline === globalCopy.multiline && regex.sticky === globalCopy.sticky, "Copy has same ignoreCase, multiline, and sticky properties");
  188. ok(XRegExp.exec("aa", globalCopy).name, "Copy retains named capture capabilities");
  189. ok(XRegExp.globalize(globalOrig).global, "Copy of global regex is global");
  190. });
  191. test("XRegExp.install", function () {
  192. expect(0);
  193. // TODO: Add tests
  194. });
  195. test("XRegExp.isInstalled", function () {
  196. expect(0);
  197. // TODO: Add tests
  198. });
  199. test("XRegExp.isRegExp", function () {
  200. ok(XRegExp.isRegExp(/(?:)/), "Regex built by regex literal is RegExp");
  201. ok(XRegExp.isRegExp(RegExp("(?:)")), "Regex built by RegExp is RegExp");
  202. ok(XRegExp.isRegExp(XRegExp("(?:)")), "Regex built by XRegExp is RegExp");
  203. ok(!XRegExp.isRegExp(undefined), "undefined is not RegExp");
  204. ok(!XRegExp.isRegExp(null), "null is not RegExp");
  205. ok(!XRegExp.isRegExp({}), "Object literal is not RegExp");
  206. ok(!XRegExp.isRegExp(function () {}), "Function literal is not RegExp");
  207. var fakeRegex = {};
  208. fakeRegex.constructor = RegExp;
  209. ok(!XRegExp.isRegExp(fakeRegex), "Object with assigned RegExp constructor is not RegExp");
  210. var tamperedRegex = /x/;
  211. tamperedRegex.constructor = {};
  212. ok(XRegExp.isRegExp(tamperedRegex), "RegExp with assigned Object constructor is RegExp");
  213. // Check whether `document` exists and only run the frame test if so. This ensures the test is
  214. // run only in the browser and not in server-side environments without a DOM.
  215. if (typeof document !== "undefined") {
  216. var iframe = document.createElement("iframe");
  217. iframe.width = iframe.height = iframe.border = 0; //iframe.style.display = "none";
  218. document.body.appendChild(iframe);
  219. frames[frames.length - 1].document.write("<script>var regex = /x/;<\/script>");
  220. ok(XRegExp.isRegExp(iframe.contentWindow.regex), "RegExp constructed in another frame is RegExp");
  221. iframe.parentNode.removeChild(iframe); // cleanup
  222. }
  223. });
  224. test("XRegExp.matchChain", function () {
  225. var html = '<html><img src="http://x.com/img.png"><script src="http://xregexp.com/path/file.ext"><img src="http://xregexp.com/path/to/img.jpg?x"><img src="http://xregexp.com/img2.gif"/></html>';
  226. var xregexpImgFileNames = XRegExp.matchChain(html, [
  227. {regex: /<img\b([^>]+)>/i, backref: 1}, // <img> tag attributes
  228. {regex: XRegExp('(?ix) \\s src=" (?<src> [^"]+ )'), backref: "src"}, // src attribute values
  229. {regex: XRegExp("^http://xregexp\\.com(/[^#?]+)", "i"), backref: 1}, // xregexp.com paths
  230. /[^\/]+$/ // filenames (strip directory paths)
  231. ]);
  232. deepEqual(xregexpImgFileNames, ["img.jpg", "img2.gif"], "Four-level chain with plain regex and regex/backref objects (using named and numbered backrefs)");
  233. deepEqual(XRegExp.matchChain("x", [/x/, /y/]), [], "Empty array returned if no matches");
  234. raises(function () {XRegExp.matchChain(html, []);}, Error, "Empty chain regex throws error");
  235. });
  236. test("XRegExp.replace", function () {
  237. equal(XRegExp.replace("test", "t", "x", "all"), "xesx", "string search with scope='all'");
  238. equal(XRegExp.replace("test", "t", "x", "one"), "xest", "string search with scope='one'");
  239. equal(XRegExp.replace("test", "t", "x"), "xest", "string search without scope");
  240. equal(XRegExp.replace("test", /t/, "x", "all"), "xesx", "regex search with scope='all'");
  241. equal(XRegExp.replace("test", /t/, "x", "one"), "xest", "regex search with scope='one'");
  242. equal(XRegExp.replace("test", /t/, "x"), "xest", "regex search without scope");
  243. equal(XRegExp.replace("test", /t/g, "x", "all"), "xesx", "global regex search with scope='all'");
  244. equal(XRegExp.replace("test", /t/g, "x", "one"), "xest", "global regex search with scope='one'");
  245. equal(XRegExp.replace("test", /t/g, "x"), "xesx", "global regex search without scope");
  246. // TODO: Add tests (above tests cover scope functionality only)
  247. });
  248. test("XRegExp.split", function () {
  249. expect(0);
  250. // TODO: Add tests
  251. });
  252. test("XRegExp.test", function () {
  253. expect(0);
  254. // TODO: Add tests
  255. });
  256. test("XRegExp.uninstall", function () {
  257. expect(0);
  258. // TODO: Add tests
  259. });
  260. test("XRegExp.union", function () {
  261. equal(XRegExp.union([XRegExp("(?<a>a)\\k<a>")], "n").test("aa"), true, "Apply flag n (test 1)");
  262. raises(function () {XRegExp.union([XRegExp("(?<a>a)\\k<a>"), /(b)\1/], "n");}, SyntaxError, "Apply flag n (test 2)");
  263. raises(function () {XRegExp.union([XRegExp("(?<a>a)\\k<a>"), /(b)\1/, XRegExp("(?<x>)")], "n");}, SyntaxError, "Apply flag n (test 3)");
  264. // TODO: Add tests
  265. });
  266. test("XRegExp.version", function () {
  267. var parts = XRegExp.version.split(".");
  268. equal(typeof XRegExp.version, "string", "Version is a string");
  269. equal(parts.length, 3, "Version is three dot-delimited parts");
  270. ok(!(isNaN(+parts[0]) || isNaN(+parts[1])), "Major and minor version parts are numeric");
  271. });
  272. //-------------------------------------------------------------------
  273. module("Overriden natives");
  274. //-------------------------------------------------------------------
  275. test("RegExp.prototype.exec", function () {
  276. XRegExp.install("natives");
  277. deepEqual(/x/.exec("a"), null, "Nonmatch returns null");
  278. deepEqual(/a/.exec("a"), ["a"], "Match returns array");
  279. deepEqual(/(a)/.exec("a"), ["a", "a"], "Match returns array with backreferences");
  280. deepEqual(/()??/.exec("a"), ["", undefined], "Backrefernces to nonparticipating capturing groups returned as undefined");
  281. equal(/a/.exec("12a").index, 2, "Match array has index set to match start");
  282. equal(/a/.exec("12a").input, "12a", "Match array has input set to target string");
  283. var regex = /x/;
  284. regex.exec("123x567");
  285. equal(regex.lastIndex, 0, "Nonglobal regex lastIndex is 0 after match");
  286. regex.lastIndex = 1;
  287. regex.exec("123x567");
  288. equal(regex.lastIndex, 1, "Nonglobal regex lastIndex is unmodified after match");
  289. regex.exec("abc");
  290. equal(regex.lastIndex, 1, "Nonglobal regex lastIndex is unmodified after failure");
  291. var regexG = /x/g;
  292. regexG.exec("123x567");
  293. equal(regexG.lastIndex, 4, "Global regex lastIndex is updated after match");
  294. regexG.lastIndex = 4;
  295. equal(regexG.exec("123x567"), null, "Global regex starts match at lastIndex");
  296. equal(regexG.lastIndex, 0, "Global regex lastIndex reset to 0 after failure");
  297. var regexZeroLength = /^/g;
  298. regexZeroLength.exec("abc");
  299. equal(regexZeroLength.lastIndex, 0, "Global regex lastIndex is not incremented after zero-length match");
  300. regexG.lastIndex = "3";
  301. deepEqual(regexG.exec("123x567"), ["x"], "lastIndex converted to integer (test 1)");
  302. regexG.lastIndex = "4";
  303. deepEqual(regexG.exec("123x567"), null, "lastIndex converted to integer (test 2)");
  304. deepEqual(/1/.exec(1), ["1"], "Numeric argument converted to string (test 1)");
  305. deepEqual(/1()/.exec(1), ["1", ""], "Numeric argument converted to string (test 2)");
  306. deepEqual(/null/.exec(null), ["null"], "null argument converted to string");
  307. deepEqual(/NaN/.exec(NaN), ["NaN"], "NaN argument converted to string");
  308. // This is broken in old Firefox (tested v2.0; it works in v8+), but not for any fault of XRegExp.
  309. // Uncomment this test if future XRegExp fixes it for old Firefox.
  310. //deepEqual(/undefined/.exec(), ["undefined"], "undefined argument converted to string");
  311. raises(function () {RegExp.prototype.exec.call("\\d", "1");}, TypeError, "TypeError thrown when context is not type RegExp");
  312. XRegExp.uninstall("natives");
  313. });
  314. test("RegExp.prototype.test", function () {
  315. XRegExp.install("natives");
  316. deepEqual(/x/.test("a"), false, "Nonmatch returns false");
  317. deepEqual(/a/.test("a"), true, "Match returns true");
  318. var regex = /x/;
  319. regex.test("123x567");
  320. equal(regex.lastIndex, 0, "Nonglobal regex lastIndex is 0 after match");
  321. regex.lastIndex = 1;
  322. regex.test("123x567");
  323. equal(regex.lastIndex, 1, "Nonglobal regex lastIndex is unmodified after match");
  324. regex.test("abc");
  325. equal(regex.lastIndex, 1, "Nonglobal regex lastIndex is unmodified after failure");
  326. var regexG = /x/g;
  327. regexG.test("123x567");
  328. equal(regexG.lastIndex, 4, "Global regex lastIndex is updated after match");
  329. regexG.lastIndex = 4;
  330. equal(regexG.test("123x567"), false, "Global regex starts match at lastIndex");
  331. equal(regexG.lastIndex, 0, "Global regex lastIndex reset to 0 after failure");
  332. var regexZeroLength = /^/g;
  333. regexZeroLength.test("abc");
  334. equal(regexZeroLength.lastIndex, 0, "Global regex lastIndex is not incremented after zero-length match");
  335. regexG.lastIndex = "3";
  336. deepEqual(regexG.test("123x567"), true, "lastIndex converted to integer (test 1)");
  337. regexG.lastIndex = "4";
  338. deepEqual(regexG.test("123x567"), false, "lastIndex converted to integer (test 2)");
  339. deepEqual(/1/.test(1), true, "Argument converted to string");
  340. raises(function () {RegExp.prototype.test.call("\\d", "1");}, TypeError, "TypeError thrown when context is not type RegExp");
  341. XRegExp.uninstall("natives");
  342. });
  343. test("String.prototype.match", function () {
  344. XRegExp.install("natives");
  345. deepEqual("a".match(/x/), null, "Nonglobal regex: Nonmatch returns null");
  346. deepEqual("a".match(/a/), ["a"], "Nonglobal regex: Match returns array");
  347. deepEqual("a".match(/(a)/), ["a", "a"], "Nonglobal regex: Match returns array with backreferences");
  348. deepEqual("a".match(/()??/), ["", undefined], "Nonglobal regex: Backrefernces to nonparticipating capturing groups returned as undefined");
  349. equal("12a".match(/a/).index, 2, "Nonglobal regex: Match array has index set to match start");
  350. equal("12a".match(/a/).input, "12a", "Nonglobal regex: Match array has input set to target string");
  351. var regex = /x/;
  352. "123x567".match(regex);
  353. equal(regex.lastIndex, 0, "Nonglobal regex: lastIndex is 0 after match");
  354. regex.lastIndex = 1;
  355. "123x567".match(regex);
  356. equal(regex.lastIndex, 1, "Nonglobal regex: lastIndex is unmodified after match");
  357. "abc".match(regex);
  358. equal(regex.lastIndex, 1, "Nonglobal regex: lastIndex is unmodified after failure");
  359. var regexG = /x/g;
  360. "123x567".match(regexG);
  361. equal(regexG.lastIndex, 0, "Global regex: lastIndex is 0 after match");
  362. regexG.lastIndex = 4;
  363. deepEqual("123x567".match(regexG), ["x"], "Global regex: Search starts at pos zero despite lastIndex");
  364. regexG.lastIndex = 4;
  365. "abc".match(regexG);
  366. equal(regexG.lastIndex, 0, "Global regex: lastIndex reset to 0 after failure");
  367. deepEqual("1".match("^(1)"), ["1", "1"], "Argument converted to RegExp");
  368. deepEqual(String.prototype.match.call(1, /1/), ["1"], "Nonstring context is converted to string");
  369. XRegExp.uninstall("natives");
  370. });
  371. test("String.prototype.replace", function () {
  372. XRegExp.install("natives");
  373. equal("xaaa".replace(/a/, "b"), "xbaa", "Basic nonglobal regex search");
  374. equal("xaaa".replace(/a/g, "b"), "xbbb", "Basic global regex search");
  375. equal("xaaa".replace("a", "b"), "xbaa", "Basic string search");
  376. equal("xaaa".replace(/a(a)/, "$1b"), "xaba", "Backreference $1 in replacement string");
  377. equal("xaaa".replace(/a(a)/, "$01b"), "xaba", "Backreference $01 in replacement string");
  378. equal("xaaa".replace(/a()()()()()()()()()(a)/, "$10b"), "xaba", "Backreference $11 in replacement string");
  379. equal("xaaa".replace(/a()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()(a)/, "$99b"), "xaba", "Backreference $99 in replacement string");
  380. equal("xaaa".replace(/a()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()(a)/, "$100b"), "x0ba", "$100 in replacement string");
  381. equal("xaaa".replace(/aa/, "$&b"), "xaaba", "Backreference $& in replacement string");
  382. equal("xaaa".replace(/aa/, "$'b"), "xaba", "Backreference $' in replacement string");
  383. equal("xaaa".replace(/aa/, "$`b"), "xxba", "Backreference $` in replacement string");
  384. equal("xaaa".replace(/aa/, "$$b"), "x$ba", "$$ in replacement string");
  385. equal("xaaa".replace("a(a)", "$1b"), "xaaa", "Parentheses in string search doesn't match");
  386. equal("xaaa".replace("aa", "$&b"), "xaaba", "Backreference $& in replacement string for string search");
  387. equal("xaaa".replace("aa", "$'b"), "xaba", "Backreference $' in replacement string for string search");
  388. equal("xaaa".replace("aa", "$`b"), "xxba", "Backreference $` in replacement string for string search");
  389. equal("xaaa".replace("aa", "$$b"), "x$ba", "$$ in replacement string for string search");
  390. equal("xaaa".replace(/a/, function () {return "b";}), "xbaa", "Nonglobal regex search with basic function replacement");
  391. equal("xaaa".replace(/a/g, function () {return "b";}), "xbbb", "Global regex search with basic function replacement");
  392. equal("xaaa".replace(/aa/, function ($0) {return $0 + "b";}), "xaaba", "Regex search with function replacement, using match");
  393. equal("xaaa".replace(/a(a)/, function ($0, $1) {return $1 + "b";}), "xaba", "Regex search with function replacement, using backreference 1");
  394. equal("xaaa".replace(/a(a)/, function ($0, $1) {return "$1b";}), "x$1ba", "Regex search with function replacement, using $1 in return string");
  395. equal("xaaa".replace(/a/, function () {return "$&b";}), "x$&baa", "Regex search with function replacement, using $& in return string");
  396. equal("xaaa".replace(/a/g, function ($0, pos) {return "" + pos;}), "x123", "Regex search with function replacement, using pos in return string");
  397. equal("xaaa".replace(/(a)/g, function ($0, $1, pos) {return "" + pos;}), "x123", "Regex (with capturing group) search with function replacement, using pos in return string");
  398. equal("xaaa".replace(/a/, function ($0, pos, str) {return str;}), "xxaaaaa", "Regex search with function replacement, using source string in return string");
  399. equal("xaaa".replace(/(a)/, function ($0, $1, pos, str) {return str;}), "xxaaaaa", "Regex (with capturing group) search with function replacement, using source string in return string");
  400. equal("xaaa".replace("a", function () {return "b";}), "xbaa", "String search with basic function replacement");
  401. equal("xaaa".replace("a", function ($0) {return $0;}), "xaaa", "String search with function replacement, using match");
  402. // This is broken in Safari (tested v5.1.2/7534.52.7), but not for any fault of XRegExp.
  403. // Uncomment this test if future XRegExp fixes it for Safari.
  404. //equal("xaaa".replace("a", function () {return "$&";}), "x$&aa", "String search with function replacement, using $& in return string");
  405. equal("xaaa".replace("a", function ($0, pos) {return "" + pos;}), "x1aa", "String search with function replacement, using pos in return string");
  406. equal("xaaa".replace("a", function ($0, pos, str) {return str;}), "xxaaaaa", "String search with function replacement, using source string in return string");
  407. equal(String.prototype.replace.call(100, /0/g, "x"), "1xx", "Number as context");
  408. equal(String.prototype.replace.call(100, /(0)/g, "$1x"), "10x0x", "Number as context with backreference $1 in replacement string");
  409. equal(String.prototype.replace.call(100, /0/g, function ($0) {return $0 + "x";}), "10x0x", "Number as context with function replacement");
  410. equal(String.prototype.replace.call(100, "0", "x"), "1x0", "String search with number as context");
  411. equal(String.prototype.replace.call(100, "0", "$&x"), "10x0", "String search with number as context, with backreference $& in replacement string");
  412. equal(String.prototype.replace.call(["a","b"], /,/g, "x"), "axb", "Array as context");
  413. equal("10x10".replace(10, "x"), "xx10", "Number as search (converted to string)");
  414. equal("xaaa,ba,b".replace(["a","b"], "x"), "xaaxa,b", "Array as search (converted to string)");
  415. equal("xaaa".replace(/a/g, 1.1), "x1.11.11.1", "Number as replacement (converted to string)");
  416. equal("xaaa".replace(/a/g, ["a","b"]), "xa,ba,ba,b", "Array as replacement (converted to string)");
  417. equal("100".replace(/0/, function ($0, pos, str) {return typeof str;}), "1string0", "typeof last argument in replacement function is string");
  418. equal(new String("100").replace(/0/, function ($0, pos, str) {return typeof str;}), "1string0", "typeof last argument in replacement function is string, when called on String as context");
  419. equal(String.prototype.replace.call(100, /0/, function ($0, pos, str) {return typeof str;}), "1string0", "typeof last argument in replacement function is string, when called on number as context");
  420. equal("xaaa".replace(/a/), "xundefinedaa", "Replacement string is 'undefined', when not provided");
  421. equal("x".replace(/x/, /x/), "/x/", "Regex search with RegExp replacement");
  422. equal("xaaa".replace(), "xaaa", "Source returned when no replacement provided");
  423. equal("test".replace(/t|(e)/g, "$1"), "es", "Numbered backreference to nonparticipating group");
  424. var regex = /x/;
  425. "123x567".replace(regex, "_");
  426. equal(regex.lastIndex, 0, "Unaltered nonglobal regex lastIndex is 0 after match");
  427. regex.lastIndex = 1;
  428. "123x567".replace(regex, "_");
  429. equal(regex.lastIndex, 1, "Nonglobal regex lastIndex is unmodified after match");
  430. "abc".replace(regex, "_");
  431. equal(regex.lastIndex, 1, "Nonglobal regex lastIndex is unmodified after failure");
  432. var regexG = /x/g;
  433. "123x567".replace(regexG, "_");
  434. equal(regexG.lastIndex, 0, "Unaltered global regex lastIndex is 0 after match");
  435. regexG.lastIndex = 5;
  436. equal("123x567".replace(regexG, "_"), "123_567", "Global regex ignores lastIndex as start position");
  437. regexG.lastIndex = 5;
  438. "123x567".replace(regexG, "_");
  439. equal(regexG.lastIndex, 0, "Global regex lastIndex reset to 0");
  440. var regex2 = /x/g;
  441. var interimLastIndex = 0;
  442. "1x2".replace(regex2, function () {
  443. interimLastIndex = regex2.lastIndex;
  444. });
  445. equal(interimLastIndex, 2, "Global regex lastIndex updated during replacement iterations");
  446. XRegExp.uninstall("natives");
  447. });
  448. test("String.prototype.split", function () {
  449. XRegExp.install("natives");
  450. expect(0);
  451. // TODO: Add tests (basic functionality tests, not the long list from
  452. // the cross-browser fixes module)
  453. XRegExp.uninstall("natives");
  454. });
  455. //-------------------------------------------------------------------
  456. module("Overriden natives extensions");
  457. //-------------------------------------------------------------------
  458. test("RegExp.prototype.exec", function () {
  459. XRegExp.install("natives");
  460. equal(XRegExp("(?<name>a)").exec("a").name, "a", "Match array has named capture properties");
  461. XRegExp.uninstall("natives");
  462. });
  463. // RegExp.prototype.test is overridden but not extended by XRegExp
  464. //test("RegExp.prototype.test", function () {});
  465. test("String.prototype.match", function () {
  466. XRegExp.install("natives");
  467. equal("a".match(XRegExp("(?<name>a)")).name, "a", "Match array has named capture properties");
  468. XRegExp.uninstall("natives");
  469. });
  470. test("String.prototype.replace", function () {
  471. XRegExp.install("natives");
  472. equal("xaaa".replace(/aa/, "$0b"), "xaaba", "$0 in replacement string works like $&");
  473. equal("xaaa".replace(/aa/, "$00b"), "xaaba", "$00 in replacement string works like $&");
  474. equal("xaaa".replace(/aa/, "$000b"), "xaa0ba", "$000 in replacement string works like $&0");
  475. raises(function () {"xaaa".replace(/aa/, "$1b");}, SyntaxError, "$1 throws in replacement string for regex with no backreference");
  476. raises(function () {"xaaa".replace(/aa/, "$01b");}, SyntaxError, "$01 throws in replacement string for regex with no backreference");
  477. equal("xaaa".replace(/aa/, "$001b"), "xaa1ba", "$001 works like $&1 in replacement string for regex with no backreference");
  478. raises(function () {"xaaa".replace(/a(a)/, "$2b");}, SyntaxError, "$2 throws in replacement string for regex with less than 2 backreferences");
  479. raises(function () {"xa(a)a".replace("a(a)", "$1b");}, SyntaxError, "$1 throws in replacement string for string search with parentheses");
  480. equal("xaaa".replace("aa", "$0b"), "xaaba", "$0 in replacement string for string search works like $&");
  481. equal("test".replace(/t|(e)/g, "${1}"), "es", "Numbered backreference in curly brackets to nonparticipating group");
  482. raises(function () {"test".replace(/t/, "${1}");}, SyntaxError, "Numbered backreference to undefined group in replacement string");
  483. equal("test".replace(XRegExp("(?<test>t)", "g"), ":${test}:"), ":t:es:t:", "Named backreference in replacement string");
  484. raises(function () {"test".replace(XRegExp("(?<test>t)", "g"), ":${x}:");}, SyntaxError, "Named backreference to undefined group in replacement string");
  485. equal("test".replace(XRegExp("(?<a>.)(?<a>.)", "g"), "${a}"), "et", "Named backreference uses last of groups with the same name");
  486. function mul(str, num) {
  487. return Array(num + 1).join(str);
  488. }
  489. // IE <= 8 doesn't allow backrefs greater than \99 in regex syntax
  490. var lottaGroups = new RegExp(
  491. "^(a)\\1" + mul("()", 8) +
  492. "(b)\\10" + mul("()", 89) +
  493. "(c)" + mul("()", 899) +
  494. "(d)$"
  495. );
  496. equal("aabbcd".replace(lottaGroups, "$0 $01 $001 $0001 $1 $10 $100 $1000"), "aabbcd a aabbcd1 aabbcd01 a b b0 b00", "Regex with 1,000 capturing groups, without curly brackets for backreferences");
  497. equal("aabbcd".replace(lottaGroups, "${0} ${01} ${001} ${0001} ${1} ${10} ${100} ${1000}"), "aabbcd a a a a b c d", "Regex with 1,000 capturing groups, with curly brackets for backreferences");
  498. // TODO: Add tests
  499. XRegExp.uninstall("natives");
  500. });
  501. // String.prototype.split is overridden but not extended by XRegExp
  502. //test("String.prototype.split", function () {});
  503. //-------------------------------------------------------------------
  504. module("New syntax and flags");
  505. //-------------------------------------------------------------------
  506. test("Named capture and backreferences", function () {
  507. expect(0);
  508. // TODO: Add tests
  509. });
  510. test("Inline comments", function () {
  511. ok(XRegExp("^a(?#)b$").test("ab"), "Comment is ignored");
  512. ok(XRegExp("^a(?#)+$").test("aaa"), "Quantifier following comment applies to preceding atom");
  513. ok(XRegExp("^(a)\\1(?#)2$").test("aa2"), "Comment separates atoms");
  514. // TODO: Add tests
  515. });
  516. test("Leading mode modifier", function () {
  517. expect(0);
  518. // TODO: Add tests
  519. });
  520. test("Enhanced error handling", function () {
  521. raises(function () {XRegExp("\\1");}, SyntaxError, "Octals throw");
  522. // TODO: Add tests
  523. // Python-style named capture syntax was added to XRegExp to avoid octal-related errors in Opera. Recent Opera supports (?P<name>..) and (?P=name) based on abandoned ES4 proposals
  524. equal(XRegExp("(?P<name>a)(b)\\2").test("abb"), true, "Numbered backreference to Python-style named capture not treated as octal (test 1)");
  525. equal(XRegExp("(?P<name>a)(b)\\1").test("aba"), true, "Numbered backreference to Python-style named capture not treated as octal (test 2)");
  526. });
  527. test("n flag (explicit capture mode)", function () {
  528. expect(0);
  529. // TODO: Add tests
  530. });
  531. test("s flag (dotall mode)", function () {
  532. expect(0);
  533. // TODO: Add tests
  534. });
  535. test("x flag (extended mode)", function () {
  536. ok(XRegExp("^a b$", "x").test("ab"), "Whitespace is ignored");
  537. ok(XRegExp("^a#comment\nb$", "x").test("ab"), "Line comment is ignored");
  538. ok(XRegExp("^a +$", "x").test("aaa"), "Quantifier following whitespace applies to preceding atom");
  539. ok(XRegExp("^(a)\\1 2$", "x").test("aa2"), "Whitespace separates atoms");
  540. ok(XRegExp("^ [ #]+ $", "x").test(" #"), "Character classes do not use free-spacing");
  541. // TODO: Add tests
  542. });
  543. //-------------------------------------------------------------------
  544. module("Cross-browser fixes");
  545. //-------------------------------------------------------------------
  546. test("Nonparticipating capture values", function () {
  547. expect(0);
  548. // TODO: Add tests
  549. });
  550. test("RegExp.prototype.lastIndex", function () {
  551. expect(0);
  552. // TODO: Add tests
  553. });
  554. test("String.prototype.split with regex separator", function () {
  555. XRegExp.install("natives");
  556. // Some of these tests are not known to fail in any browser, but many fail in at least one
  557. // version of one browser.
  558. deepEqual("".split(), [""]);
  559. deepEqual("".split(/./), [""]);
  560. deepEqual("".split(/.?/), []);
  561. deepEqual("".split(/.??/), []);
  562. deepEqual("ab".split(/a*/), ["", "b"]);
  563. deepEqual("ab".split(/a*?/), ["a", "b"]);
  564. deepEqual("ab".split(/(?:ab)/), ["", ""]);
  565. deepEqual("ab".split(/(?:ab)*/), ["", ""]);
  566. deepEqual("ab".split(/(?:ab)*?/), ["a", "b"]);
  567. deepEqual("test".split(""), ["t", "e", "s", "t"]);
  568. deepEqual("test".split(), ["test"]);
  569. deepEqual("111".split(1), ["", "", "", ""]);
  570. deepEqual("test".split(/(?:)/, 2), ["t", "e"]);
  571. deepEqual("test".split(/(?:)/, -1), ["t", "e", "s", "t"]);
  572. deepEqual("test".split(/(?:)/, undefined), ["t", "e", "s", "t"]);
  573. deepEqual("test".split(/(?:)/, null), []);
  574. deepEqual("test".split(/(?:)/, NaN), []);
  575. deepEqual("test".split(/(?:)/, true), ["t"]);
  576. deepEqual("test".split(/(?:)/, "2"), ["t", "e"]);
  577. deepEqual("test".split(/(?:)/, "two"), []);
  578. deepEqual("a".split(/-/), ["a"]);
  579. deepEqual("a".split(/-?/), ["a"]);
  580. deepEqual("a".split(/-??/), ["a"]);
  581. deepEqual("a".split(/a/), ["", ""]);
  582. deepEqual("a".split(/a?/), ["", ""]);
  583. deepEqual("a".split(/a??/), ["a"]);
  584. deepEqual("ab".split(/-/), ["ab"]);
  585. deepEqual("ab".split(/-?/), ["a", "b"]);
  586. deepEqual("ab".split(/-??/), ["a", "b"]);
  587. deepEqual("a-b".split(/-/), ["a", "b"]);
  588. deepEqual("a-b".split(/-?/), ["a", "b"]);
  589. deepEqual("a-b".split(/-??/), ["a", "-", "b"]);
  590. deepEqual("a--b".split(/-/), ["a", "", "b"]);
  591. deepEqual("a--b".split(/-?/), ["a", "", "b"]);
  592. deepEqual("a--b".split(/-??/), ["a", "-", "-", "b"]);
  593. deepEqual("".split(/()()/), []);
  594. deepEqual(".".split(/()()/), ["."]);
  595. deepEqual(".".split(/(.?)(.?)/), ["", ".", "", ""]);
  596. deepEqual(".".split(/(.??)(.??)/), ["."]);
  597. deepEqual(".".split(/(.)?(.)?/), ["", ".", undefined, ""]);
  598. deepEqual("A<B>bold</B>and<CODE>coded</CODE>".split(/<(\/)?([^<>]+)>/), ["A", undefined, "B", "bold", "/", "B", "and", undefined, "CODE", "coded", "/", "CODE", ""]);
  599. deepEqual("test".split(/(.?)/), ["","t","","e","","s","","t",""]);
  600. deepEqual("tesst".split(/(s)*/), ["t", undefined, "e", "s", "t"]);
  601. deepEqual("tesst".split(/(s)*?/), ["t", undefined, "e", undefined, "s", undefined, "s", undefined, "t"]);
  602. deepEqual("tesst".split(/(s*)/), ["t", "", "e", "ss", "t"]);
  603. deepEqual("tesst".split(/(s*?)/), ["t", "", "e", "", "s", "", "s", "", "t"]);
  604. deepEqual("tesst".split(/(?:s)*/), ["t", "e", "t"]);
  605. deepEqual("tesst".split(/(?=s+)/), ["te", "s", "st"]);
  606. deepEqual("test".split("t"), ["", "es", ""]);
  607. deepEqual("test".split("es"), ["t", "t"]);
  608. deepEqual("test".split(/t/), ["", "es", ""]);
  609. deepEqual("test".split(/es/), ["t", "t"]);
  610. deepEqual("test".split(/(t)/), ["", "t", "es", "t", ""]);
  611. deepEqual("test".split(/(es)/), ["t", "es", "t"]);
  612. deepEqual("test".split(/(t)(e)(s)(t)/), ["", "t", "e", "s", "t", ""]);
  613. deepEqual(".".split(/(((.((.??)))))/), ["", ".", ".", ".", "", "", ""]);
  614. deepEqual(".".split(/(((((.??)))))/), ["."]);
  615. deepEqual("a b c d".split(/ /, -(Math.pow(2, 32) - 1)), ["a"]); // very large negative number test by Brian O
  616. deepEqual("a b c d".split(/ /, Math.pow(2, 32) + 1), ["a"]);
  617. deepEqual("a b c d".split(/ /, Infinity), []);
  618. XRegExp.uninstall("natives");
  619. });
  620. test("Regular expression syntax", function () {
  621. expect(0);
  622. // TODO: Add tests
  623. });
  624. test("Replacement text syntax", function () {
  625. expect(0);
  626. // TODO: Add tests
  627. });
  628. test("Type conversion", function () {
  629. XRegExp.install("natives");
  630. // these are duplicated from String.prototype.replace tests in the overridden natives module
  631. equal(new String("100").replace(/0/, function ($0, pos, str) {return typeof str;}), "1string0", "String.prototype.replace: typeof last argument in replacement function is string, when called on String as context");
  632. equal(String.prototype.replace.call(100, /0/, function ($0, pos, str) {return typeof str;}), "1string0", "String.prototype.replace: typeof last argument in replacement function is string, when called on number as context");
  633. // TODO: Add tests
  634. XRegExp.uninstall("natives");
  635. });
  636. //-------------------------------------------------------------------
  637. module("Addons");
  638. //-------------------------------------------------------------------
  639. test("Unicode base", function () {
  640. expect(0);
  641. // TODO: Add tests
  642. });
  643. test("Unicode categories", function () {
  644. expect(0);
  645. // TODO: Add tests
  646. });
  647. test("Unicode scripts", function () {
  648. expect(0);
  649. // TODO: Add tests
  650. });
  651. test("Unicode blocks", function () {
  652. expect(0);
  653. // TODO: Add tests
  654. });
  655. test("Unicode properties", function () {
  656. expect(0);
  657. // TODO: Add tests
  658. });
  659. test("XRegExp.matchRecursive", function () {
  660. ok(XRegExp.matchRecursive, "XRegExp.matchRecursive exists");
  661. // TODO: Add tests
  662. });
  663. test("XRegExp.build", function () {
  664. ok(XRegExp.build, "XRegExp.build exists");
  665. var built = XRegExp.build("({{n1}})\\1(?<nX>{{n2}})\\2()\\3\\1\\2\\k<nX>", {
  666. n1: XRegExp("(?<yo>a)\\1"),
  667. n2: XRegExp("(?<yo>b)\\1")
  668. }); // Equivalent to XRegExp("(?<n1>(?<yo>a)\\2)\\1(?<nX>(?<yo>b)\\4)\\3()\\5\\1\\3\\k<nX>")
  669. var match = XRegExp.exec("aaaabbbbaabbbb", built);
  670. ok(match);
  671. equal(match.n1, "aa");
  672. equal(match.n2, undefined);
  673. equal(match.nX, "bb");
  674. equal(match.yo, "b");
  675. // IE v7-8 (not v6 or v9) throws an Error rather than SyntaxError
  676. raises(function () {var r = XRegExp.build('(?x)({{a}})', {a: /#/});}, Error, "Mode modifier in outer pattern applies to full regex with interpolated values (test 1)");
  677. equal(XRegExp.build("(?x){{a}}", {a: /1 2/}).test("12"), true, "Mode modifier in outer pattern applies to full regex with interpolated values (test 2)");
  678. equal(XRegExp.build("(?m){{a}}", {a: /a/}).multiline, true, "Mode modifier with native flag in outer pattern is applied to the final result");
  679. equal(XRegExp.build("^[{{a}}]$", {a: "x"}).test("x"), false, "Named subpattern not interpolated within character class (test 1)");
  680. equal(XRegExp.build("^{{a}}[{{a}}]$", {a: "x"}).test("x{"), true, "Named subpattern not interpolated within character class (test 2)");
  681. // TODO: Add tests
  682. });
  683. test("XRegExp.prototype.apply", function () {
  684. var regex = XRegExp("x");
  685. ok(XRegExp.prototype.apply, "XRegExp.prototype.apply exists");
  686. deepEqual(regex.apply(null, ["x"]), regex.test("x"), "Apply with match same as test");
  687. deepEqual(regex.apply(null, ["y"]), regex.test("y"), "Apply without match same as test");
  688. });
  689. test("XRegExp.prototype.call", function () {
  690. var regex = XRegExp("x");
  691. ok(XRegExp.prototype.call, "XRegExp.prototype.call exists");
  692. deepEqual(regex.call(null, "x"), regex.test("x"), "Call with match same as test");
  693. deepEqual(regex.call(null, "y"), regex.test("y"), "Call without match same as test");
  694. });
  695. test("XRegExp.prototype.forEach", function () {
  696. ok(XRegExp.prototype.forEach, "XRegExp.prototype.forEach exists");
  697. // TODO: Add tests
  698. });
  699. test("XRegExp.prototype.globalize", function () {
  700. ok(XRegExp.prototype.globalize, "XRegExp.prototype.globalize exists");
  701. // TODO: Add tests
  702. });
  703. test("XRegExp.prototype.xexec", function () {
  704. ok(XRegExp.prototype.xexec, "XRegExp.prototype.xexec exists");
  705. // TODO: Add tests
  706. });
  707. test("XRegExp.prototype.xtest", function () {
  708. ok(XRegExp.prototype.xtest, "XRegExp.prototype.xtest exists");
  709. // TODO: Add tests
  710. });