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