1(function(QUnit) { 2 3 var router = null; 4 var location = null; 5 var lastRoute = null; 6 var lastArgs = []; 7 8 var onRoute = function(routerParam, route, args) { 9 lastRoute = route; 10 lastArgs = args; 11 }; 12 13 var Location = function(href) { 14 this.replace(href); 15 }; 16 17 _.extend(Location.prototype, { 18 19 parser: document.createElement('a'), 20 21 replace: function(href) { 22 this.parser.href = href; 23 _.extend(this, _.pick(this.parser, 24 'href', 25 'hash', 26 'host', 27 'search', 28 'fragment', 29 'pathname', 30 'protocol' 31 )); 32 33 // In IE, anchor.pathname does not contain a leading slash though 34 // window.location.pathname does. 35 if (!/^\//.test(this.pathname)) this.pathname = '/' + this.pathname; 36 }, 37 38 toString: function() { 39 return this.href; 40 } 41 42 }); 43 44 QUnit.module('Backbone.Router', { 45 46 beforeEach: function() { 47 location = new Location('http://example.com'); 48 Backbone.history = _.extend(new Backbone.History, {location: location}); 49 router = new Router({testing: 101}); 50 Backbone.history.interval = 9; 51 Backbone.history.start({pushState: false}); 52 lastRoute = null; 53 lastArgs = []; 54 Backbone.history.on('route', onRoute); 55 }, 56 57 afterEach: function() { 58 Backbone.history.stop(); 59 Backbone.history.off('route', onRoute); 60 } 61 62 }); 63 64 var ExternalObject = { 65 value: 'unset', 66 67 routingFunction: function(value) { 68 this.value = value; 69 } 70 }; 71 ExternalObject.routingFunction = _.bind(ExternalObject.routingFunction, ExternalObject); 72 73 var Router = Backbone.Router.extend({ 74 75 count: 0, 76 77 routes: { 78 'noCallback': 'noCallback', 79 'counter': 'counter', 80 'search/:query': 'search', 81 'search/:query/p:page': 'search', 82 'charñ': 'charUTF', 83 'char%C3%B1': 'charEscaped', 84 'contacts': 'contacts', 85 'contacts/new': 'newContact', 86 'contacts/:id': 'loadContact', 87 'route-event/:arg': 'routeEvent', 88 'optional(/:item)': 'optionalItem', 89 'named/optional/(y:z)': 'namedOptional', 90 'splat/*args/end': 'splat', 91 ':repo/compare/*from...*to': 'github', 92 'decode/:named/*splat': 'decode', 93 '*first/complex-*part/*rest': 'complex', 94 'query/:entity': 'query', 95 'function/:value': ExternalObject.routingFunction, 96 '*anything': 'anything' 97 }, 98 99 preinitialize: function(options) { 100 this.testpreinit = 'foo'; 101 }, 102 103 initialize: function(options) { 104 this.testing = options.testing; 105 this.route('implicit', 'implicit'); 106 }, 107 108 counter: function() { 109 this.count++; 110 }, 111 112 implicit: function() { 113 this.count++; 114 }, 115 116 search: function(query, page) { 117 this.query = query; 118 this.page = page; 119 }, 120 121 charUTF: function() { 122 this.charType = 'UTF'; 123 }, 124 125 charEscaped: function() { 126 this.charType = 'escaped'; 127 }, 128 129 contacts: function() { 130 this.contact = 'index'; 131 }, 132 133 newContact: function() { 134 this.contact = 'new'; 135 }, 136 137 loadContact: function() { 138 this.contact = 'load'; 139 }, 140 141 optionalItem: function(arg) { 142 this.arg = arg !== void 0 ? arg : null; 143 }, 144 145 splat: function(args) { 146 this.args = args; 147 }, 148 149 github: function(repo, from, to) { 150 this.repo = repo; 151 this.from = from; 152 this.to = to; 153 }, 154 155 complex: function(first, part, rest) { 156 this.first = first; 157 this.part = part; 158 this.rest = rest; 159 }, 160 161 query: function(entity, args) { 162 this.entity = entity; 163 this.queryArgs = args; 164 }, 165 166 anything: function(whatever) { 167 this.anything = whatever; 168 }, 169 170 namedOptional: function(z) { 171 this.z = z; 172 }, 173 174 decode: function(named, path) { 175 this.named = named; 176 this.path = path; 177 }, 178 179 routeEvent: function(arg) { 180 } 181 182 }); 183 184 QUnit.test('initialize', function(assert) { 185 assert.expect(1); 186 assert.equal(router.testing, 101); 187 }); 188 189 QUnit.test('preinitialize', function(assert) { 190 assert.expect(1); 191 assert.equal(router.testpreinit, 'foo'); 192 }); 193 194 QUnit.test('routes (simple)', function(assert) { 195 assert.expect(4); 196 location.replace('http://example.com#search/news'); 197 Backbone.history.checkUrl(); 198 assert.equal(router.query, 'news'); 199 assert.equal(router.page, void 0); 200 assert.equal(lastRoute, 'search'); 201 assert.equal(lastArgs[0], 'news'); 202 }); 203 204 QUnit.test('routes (simple, but unicode)', function(assert) { 205 assert.expect(4); 206 location.replace('http://example.com#search/тест'); 207 Backbone.history.checkUrl(); 208 assert.equal(router.query, 'тест'); 209 assert.equal(router.page, void 0); 210 assert.equal(lastRoute, 'search'); 211 assert.equal(lastArgs[0], 'тест'); 212 }); 213 214 QUnit.test('routes (two part)', function(assert) { 215 assert.expect(2); 216 location.replace('http://example.com#search/nyc/p10'); 217 Backbone.history.checkUrl(); 218 assert.equal(router.query, 'nyc'); 219 assert.equal(router.page, '10'); 220 }); 221 222 QUnit.test('routes via navigate', function(assert) { 223 assert.expect(2); 224 Backbone.history.navigate('search/manhattan/p20', {trigger: true}); 225 assert.equal(router.query, 'manhattan'); 226 assert.equal(router.page, '20'); 227 }); 228 229 QUnit.test('routes via navigate with params', function(assert) { 230 assert.expect(1); 231 Backbone.history.navigate('query/test?a=b', {trigger: true}); 232 assert.equal(router.queryArgs, 'a=b'); 233 }); 234 235 QUnit.test('routes via navigate for backwards-compatibility', function(assert) { 236 assert.expect(2); 237 Backbone.history.navigate('search/manhattan/p20', true); 238 assert.equal(router.query, 'manhattan'); 239 assert.equal(router.page, '20'); 240 }); 241 242 QUnit.test('reports matched route via nagivate', function(assert) { 243 assert.expect(1); 244 assert.ok(Backbone.history.navigate('search/manhattan/p20', true)); 245 }); 246 247 QUnit.test('route precedence via navigate', function(assert) { 248 assert.expect(6); 249 250 // Check both 0.9.x and backwards-compatibility options 251 _.each([{trigger: true}, true], function(options) { 252 Backbone.history.navigate('contacts', options); 253 assert.equal(router.contact, 'index'); 254 Backbone.history.navigate('contacts/new', options); 255 assert.equal(router.contact, 'new'); 256 Backbone.history.navigate('contacts/foo', options); 257 assert.equal(router.contact, 'load'); 258 }); 259 }); 260 261 QUnit.test('loadUrl is not called for identical routes.', function(assert) { 262 assert.expect(0); 263 Backbone.history.loadUrl = function() { assert.ok(false); }; 264 location.replace('http://example.com#route'); 265 Backbone.history.navigate('route'); 266 Backbone.history.navigate('/route'); 267 Backbone.history.navigate('/route'); 268 }); 269 270 QUnit.test('use implicit callback if none provided', function(assert) { 271 assert.expect(1); 272 router.count = 0; 273 router.navigate('implicit', {trigger: true}); 274 assert.equal(router.count, 1); 275 }); 276 277 QUnit.test('routes via navigate with {replace: true}', function(assert) { 278 assert.expect(1); 279 location.replace('http://example.com#start_here'); 280 Backbone.history.checkUrl(); 281 location.replace = function(href) { 282 assert.strictEqual(href, new Location('http://example.com#end_here').href); 283 }; 284 Backbone.history.navigate('end_here', {replace: true}); 285 }); 286 287 QUnit.test('routes (splats)', function(assert) { 288 assert.expect(1); 289 location.replace('http://example.com#splat/long-list/of/splatted_99args/end'); 290 Backbone.history.checkUrl(); 291 assert.equal(router.args, 'long-list/of/splatted_99args'); 292 }); 293 294 QUnit.test('routes (github)', function(assert) { 295 assert.expect(3); 296 location.replace('http://example.com#backbone/compare/1.0...braddunbar:with/slash'); 297 Backbone.history.checkUrl(); 298 assert.equal(router.repo, 'backbone'); 299 assert.equal(router.from, '1.0'); 300 assert.equal(router.to, 'braddunbar:with/slash'); 301 }); 302 303 QUnit.test('routes (optional)', function(assert) { 304 assert.expect(2); 305 location.replace('http://example.com#optional'); 306 Backbone.history.checkUrl(); 307 assert.ok(!router.arg); 308 location.replace('http://example.com#optional/thing'); 309 Backbone.history.checkUrl(); 310 assert.equal(router.arg, 'thing'); 311 }); 312 313 QUnit.test('routes (complex)', function(assert) { 314 assert.expect(3); 315 location.replace('http://example.com#one/two/three/complex-part/four/five/six/seven'); 316 Backbone.history.checkUrl(); 317 assert.equal(router.first, 'one/two/three'); 318 assert.equal(router.part, 'part'); 319 assert.equal(router.rest, 'four/five/six/seven'); 320 }); 321 322 QUnit.test('routes (query)', function(assert) { 323 assert.expect(5); 324 location.replace('http://example.com#query/mandel?a=b&c=d'); 325 Backbone.history.checkUrl(); 326 assert.equal(router.entity, 'mandel'); 327 assert.equal(router.queryArgs, 'a=b&c=d'); 328 assert.equal(lastRoute, 'query'); 329 assert.equal(lastArgs[0], 'mandel'); 330 assert.equal(lastArgs[1], 'a=b&c=d'); 331 }); 332 333 QUnit.test('routes (anything)', function(assert) { 334 assert.expect(1); 335 location.replace('http://example.com#doesnt-match-a-route'); 336 Backbone.history.checkUrl(); 337 assert.equal(router.anything, 'doesnt-match-a-route'); 338 }); 339 340 QUnit.test('routes (function)', function(assert) { 341 assert.expect(3); 342 router.on('route', function(name) { 343 assert.ok(name === ''); 344 }); 345 assert.equal(ExternalObject.value, 'unset'); 346 location.replace('http://example.com#function/set'); 347 Backbone.history.checkUrl(); 348 assert.equal(ExternalObject.value, 'set'); 349 }); 350 351 QUnit.test('Decode named parameters, not splats.', function(assert) { 352 assert.expect(2); 353 location.replace('http://example.com#decode/a%2Fb/c%2Fd/e'); 354 Backbone.history.checkUrl(); 355 assert.strictEqual(router.named, 'a/b'); 356 assert.strictEqual(router.path, 'c/d/e'); 357 }); 358 359 QUnit.test('fires event when router doesn\'t have callback on it', function(assert) { 360 assert.expect(1); 361 router.on('route:noCallback', function() { assert.ok(true); }); 362 location.replace('http://example.com#noCallback'); 363 Backbone.history.checkUrl(); 364 }); 365 366 QUnit.test('No events are triggered if #execute returns false.', function(assert) { 367 assert.expect(1); 368 var MyRouter = Backbone.Router.extend({ 369 370 routes: { 371 foo: function() { 372 assert.ok(true); 373 } 374 }, 375 376 execute: function(callback, args) { 377 callback.apply(this, args); 378 return false; 379 } 380 381 }); 382 383 var myRouter = new MyRouter; 384 385 myRouter.on('route route:foo', function() { 386 assert.ok(false); 387 }); 388 389 Backbone.history.on('route', function() { 390 assert.ok(false); 391 }); 392 393 location.replace('http://example.com#foo'); 394 Backbone.history.checkUrl(); 395 }); 396 397 QUnit.test('#933, #908 - leading slash', function(assert) { 398 assert.expect(2); 399 location.replace('http://example.com/root/foo'); 400 401 Backbone.history.stop(); 402 Backbone.history = _.extend(new Backbone.History, {location: location}); 403 Backbone.history.start({root: '/root', hashChange: false, silent: true}); 404 assert.strictEqual(Backbone.history.getFragment(), 'foo'); 405 406 Backbone.history.stop(); 407 Backbone.history = _.extend(new Backbone.History, {location: location}); 408 Backbone.history.start({root: '/root/', hashChange: false, silent: true}); 409 assert.strictEqual(Backbone.history.getFragment(), 'foo'); 410 }); 411 412 QUnit.test('#967 - Route callback gets passed encoded values.', function(assert) { 413 assert.expect(3); 414 var route = 'has%2Fslash/complex-has%23hash/has%20space'; 415 Backbone.history.navigate(route, {trigger: true}); 416 assert.strictEqual(router.first, 'has/slash'); 417 assert.strictEqual(router.part, 'has#hash'); 418 assert.strictEqual(router.rest, 'has space'); 419 }); 420 421 QUnit.test('correctly handles URLs with % (#868)', function(assert) { 422 assert.expect(3); 423 location.replace('http://example.com#search/fat%3A1.5%25'); 424 Backbone.history.checkUrl(); 425 location.replace('http://example.com#search/fat'); 426 Backbone.history.checkUrl(); 427 assert.equal(router.query, 'fat'); 428 assert.equal(router.page, void 0); 429 assert.equal(lastRoute, 'search'); 430 }); 431 432 QUnit.test('#2666 - Hashes with UTF8 in them.', function(assert) { 433 assert.expect(2); 434 Backbone.history.navigate('charñ', {trigger: true}); 435 assert.equal(router.charType, 'UTF'); 436 Backbone.history.navigate('char%C3%B1', {trigger: true}); 437 assert.equal(router.charType, 'UTF'); 438 }); 439 440 QUnit.test('#1185 - Use pathname when hashChange is not wanted.', function(assert) { 441 assert.expect(1); 442 Backbone.history.stop(); 443 location.replace('http://example.com/path/name#hash'); 444 Backbone.history = _.extend(new Backbone.History, {location: location}); 445 Backbone.history.start({hashChange: false}); 446 var fragment = Backbone.history.getFragment(); 447 assert.strictEqual(fragment, location.pathname.replace(/^\//, '')); 448 }); 449 450 QUnit.test('#1206 - Strip leading slash before location.assign.', function(assert) { 451 assert.expect(1); 452 Backbone.history.stop(); 453 location.replace('http://example.com/root/'); 454 Backbone.history = _.extend(new Backbone.History, {location: location}); 455 Backbone.history.start({hashChange: false, root: '/root/'}); 456 location.assign = function(pathname) { 457 assert.strictEqual(pathname, '/root/fragment'); 458 }; 459 Backbone.history.navigate('/fragment'); 460 }); 461 462 QUnit.test('#1387 - Root fragment without trailing slash.', function(assert) { 463 assert.expect(1); 464 Backbone.history.stop(); 465 location.replace('http://example.com/root'); 466 Backbone.history = _.extend(new Backbone.History, {location: location}); 467 Backbone.history.start({hashChange: false, root: '/root/', silent: true}); 468 assert.strictEqual(Backbone.history.getFragment(), ''); 469 }); 470 471 QUnit.test('#1366 - History does not prepend root to fragment.', function(assert) { 472 assert.expect(2); 473 Backbone.history.stop(); 474 location.replace('http://example.com/root/'); 475 Backbone.history = _.extend(new Backbone.History, { 476 location: location, 477 history: { 478 pushState: function(state, title, url) { 479 assert.strictEqual(url, '/root/x'); 480 } 481 } 482 }); 483 Backbone.history.start({ 484 root: '/root/', 485 pushState: true, 486 hashChange: false 487 }); 488 Backbone.history.navigate('x'); 489 assert.strictEqual(Backbone.history.fragment, 'x'); 490 }); 491 492 QUnit.test('Normalize root.', function(assert) { 493 assert.expect(1); 494 Backbone.history.stop(); 495 location.replace('http://example.com/root'); 496 Backbone.history = _.extend(new Backbone.History, { 497 location: location, 498 history: { 499 pushState: function(state, title, url) { 500 assert.strictEqual(url, '/root/fragment'); 501 } 502 } 503 }); 504 Backbone.history.start({ 505 pushState: true, 506 root: '/root', 507 hashChange: false 508 }); 509 Backbone.history.navigate('fragment'); 510 }); 511 512 QUnit.test('Normalize root.', function(assert) { 513 assert.expect(1); 514 Backbone.history.stop(); 515 location.replace('http://example.com/root#fragment'); 516 Backbone.history = _.extend(new Backbone.History, { 517 location: location, 518 history: { 519 pushState: function(state, title, url) {}, 520 replaceState: function(state, title, url) { 521 assert.strictEqual(url, '/root/fragment'); 522 } 523 } 524 }); 525 Backbone.history.start({ 526 pushState: true, 527 root: '/root' 528 }); 529 }); 530 531 QUnit.test('Normalize root.', function(assert) { 532 assert.expect(1); 533 Backbone.history.stop(); 534 location.replace('http://example.com/root'); 535 Backbone.history = _.extend(new Backbone.History, {location: location}); 536 Backbone.history.loadUrl = function() { assert.ok(true); }; 537 Backbone.history.start({ 538 pushState: true, 539 root: '/root' 540 }); 541 }); 542 543 QUnit.test('Normalize root - leading slash.', function(assert) { 544 assert.expect(1); 545 Backbone.history.stop(); 546 location.replace('http://example.com/root'); 547 Backbone.history = _.extend(new Backbone.History, { 548 location: location, 549 history: { 550 pushState: function() {}, 551 replaceState: function() {} 552 } 553 }); 554 Backbone.history.start({root: 'root'}); 555 assert.strictEqual(Backbone.history.root, '/root/'); 556 }); 557 558 QUnit.test('Transition from hashChange to pushState.', function(assert) { 559 assert.expect(1); 560 Backbone.history.stop(); 561 location.replace('http://example.com/root#x/y'); 562 Backbone.history = _.extend(new Backbone.History, { 563 location: location, 564 history: { 565 pushState: function() {}, 566 replaceState: function(state, title, url) { 567 assert.strictEqual(url, '/root/x/y'); 568 } 569 } 570 }); 571 Backbone.history.start({ 572 root: 'root', 573 pushState: true 574 }); 575 }); 576 577 QUnit.test('#1619: Router: Normalize empty root', function(assert) { 578 assert.expect(1); 579 Backbone.history.stop(); 580 location.replace('http://example.com/'); 581 Backbone.history = _.extend(new Backbone.History, { 582 location: location, 583 history: { 584 pushState: function() {}, 585 replaceState: function() {} 586 } 587 }); 588 Backbone.history.start({root: ''}); 589 assert.strictEqual(Backbone.history.root, '/'); 590 }); 591 592 QUnit.test('#1619: Router: nagivate with empty root', function(assert) { 593 assert.expect(1); 594 Backbone.history.stop(); 595 location.replace('http://example.com/'); 596 Backbone.history = _.extend(new Backbone.History, { 597 location: location, 598 history: { 599 pushState: function(state, title, url) { 600 assert.strictEqual(url, '/fragment'); 601 } 602 } 603 }); 604 Backbone.history.start({ 605 pushState: true, 606 root: '', 607 hashChange: false 608 }); 609 Backbone.history.navigate('fragment'); 610 }); 611 612 QUnit.test('Transition from pushState to hashChange.', function(assert) { 613 assert.expect(1); 614 Backbone.history.stop(); 615 location.replace('http://example.com/root/x/y?a=b'); 616 location.replace = function(url) { 617 assert.strictEqual(url, '/root#x/y?a=b'); 618 }; 619 Backbone.history = _.extend(new Backbone.History, { 620 location: location, 621 history: { 622 pushState: null, 623 replaceState: null 624 } 625 }); 626 Backbone.history.start({ 627 root: 'root', 628 pushState: true 629 }); 630 }); 631 632 QUnit.test('#1695 - hashChange to pushState with search.', function(assert) { 633 assert.expect(1); 634 Backbone.history.stop(); 635 location.replace('http://example.com/root#x/y?a=b'); 636 Backbone.history = _.extend(new Backbone.History, { 637 location: location, 638 history: { 639 pushState: function() {}, 640 replaceState: function(state, title, url) { 641 assert.strictEqual(url, '/root/x/y?a=b'); 642 } 643 } 644 }); 645 Backbone.history.start({ 646 root: 'root', 647 pushState: true 648 }); 649 }); 650 651 QUnit.test('#1746 - Router allows empty route.', function(assert) { 652 assert.expect(1); 653 var MyRouter = Backbone.Router.extend({ 654 routes: {'': 'empty'}, 655 empty: function() {}, 656 route: function(route) { 657 assert.strictEqual(route, ''); 658 } 659 }); 660 new MyRouter; 661 }); 662 663 QUnit.test('#1794 - Trailing space in fragments.', function(assert) { 664 assert.expect(1); 665 var history = new Backbone.History; 666 assert.strictEqual(history.getFragment('fragment '), 'fragment'); 667 }); 668 669 QUnit.test('#1820 - Leading slash and trailing space.', function(assert) { 670 assert.expect(1); 671 var history = new Backbone.History; 672 assert.strictEqual(history.getFragment('/fragment '), 'fragment'); 673 }); 674 675 QUnit.test('#1980 - Optional parameters.', function(assert) { 676 assert.expect(2); 677 location.replace('http://example.com#named/optional/y'); 678 Backbone.history.checkUrl(); 679 assert.strictEqual(router.z, undefined); 680 location.replace('http://example.com#named/optional/y123'); 681 Backbone.history.checkUrl(); 682 assert.strictEqual(router.z, '123'); 683 }); 684 685 QUnit.test('#2062 - Trigger "route" event on router instance.', function(assert) { 686 assert.expect(2); 687 router.on('route', function(name, args) { 688 assert.strictEqual(name, 'routeEvent'); 689 assert.deepEqual(args, ['x', null]); 690 }); 691 location.replace('http://example.com#route-event/x'); 692 Backbone.history.checkUrl(); 693 }); 694 695 QUnit.test('#2255 - Extend routes by making routes a function.', function(assert) { 696 assert.expect(1); 697 var RouterBase = Backbone.Router.extend({ 698 routes: function() { 699 return { 700 home: 'root', 701 index: 'index.html' 702 }; 703 } 704 }); 705 706 var RouterExtended = RouterBase.extend({ 707 routes: function() { 708 var _super = RouterExtended.__super__.routes; 709 return _.extend(_super(), {show: 'show', search: 'search'}); 710 } 711 }); 712 713 var myRouter = new RouterExtended(); 714 assert.deepEqual({home: 'root', index: 'index.html', show: 'show', search: 'search'}, myRouter.routes); 715 }); 716 717 QUnit.test('#2538 - hashChange to pushState only if both requested.', function(assert) { 718 assert.expect(0); 719 Backbone.history.stop(); 720 location.replace('http://example.com/root?a=b#x/y'); 721 Backbone.history = _.extend(new Backbone.History, { 722 location: location, 723 history: { 724 pushState: function() {}, 725 replaceState: function() { assert.ok(false); } 726 } 727 }); 728 Backbone.history.start({ 729 root: 'root', 730 pushState: true, 731 hashChange: false 732 }); 733 }); 734 735 QUnit.test('No hash fallback.', function(assert) { 736 assert.expect(0); 737 Backbone.history.stop(); 738 Backbone.history = _.extend(new Backbone.History, { 739 location: location, 740 history: { 741 pushState: function() {}, 742 replaceState: function() {} 743 } 744 }); 745 746 var MyRouter = Backbone.Router.extend({ 747 routes: { 748 hash: function() { assert.ok(false); } 749 } 750 }); 751 var myRouter = new MyRouter; 752 753 location.replace('http://example.com/'); 754 Backbone.history.start({ 755 pushState: true, 756 hashChange: false 757 }); 758 location.replace('http://example.com/nomatch#hash'); 759 Backbone.history.checkUrl(); 760 }); 761 762 QUnit.test('#2656 - No trailing slash on root.', function(assert) { 763 assert.expect(1); 764 Backbone.history.stop(); 765 Backbone.history = _.extend(new Backbone.History, { 766 location: location, 767 history: { 768 pushState: function(state, title, url) { 769 assert.strictEqual(url, '/root'); 770 } 771 } 772 }); 773 location.replace('http://example.com/root/path'); 774 Backbone.history.start({pushState: true, hashChange: false, root: 'root'}); 775 Backbone.history.navigate(''); 776 }); 777 778 QUnit.test('#2656 - No trailing slash on root.', function(assert) { 779 assert.expect(1); 780 Backbone.history.stop(); 781 Backbone.history = _.extend(new Backbone.History, { 782 location: location, 783 history: { 784 pushState: function(state, title, url) { 785 assert.strictEqual(url, '/'); 786 } 787 } 788 }); 789 location.replace('http://example.com/path'); 790 Backbone.history.start({pushState: true, hashChange: false}); 791 Backbone.history.navigate(''); 792 }); 793 794 QUnit.test('#2656 - No trailing slash on root.', function(assert) { 795 assert.expect(1); 796 Backbone.history.stop(); 797 Backbone.history = _.extend(new Backbone.History, { 798 location: location, 799 history: { 800 pushState: function(state, title, url) { 801 assert.strictEqual(url, '/root?x=1'); 802 } 803 } 804 }); 805 location.replace('http://example.com/root/path'); 806 Backbone.history.start({pushState: true, hashChange: false, root: 'root'}); 807 Backbone.history.navigate('?x=1'); 808 }); 809 810 QUnit.test('#2765 - Fragment matching sans query/hash.', function(assert) { 811 assert.expect(2); 812 Backbone.history.stop(); 813 Backbone.history = _.extend(new Backbone.History, { 814 location: location, 815 history: { 816 pushState: function(state, title, url) { 817 assert.strictEqual(url, '/path?query#hash'); 818 } 819 } 820 }); 821 822 var MyRouter = Backbone.Router.extend({ 823 routes: { 824 path: function() { assert.ok(true); } 825 } 826 }); 827 var myRouter = new MyRouter; 828 829 location.replace('http://example.com/'); 830 Backbone.history.start({pushState: true, hashChange: false}); 831 Backbone.history.navigate('path?query#hash', true); 832 }); 833 834 QUnit.test('Do not decode the search params.', function(assert) { 835 assert.expect(1); 836 var MyRouter = Backbone.Router.extend({ 837 routes: { 838 path: function(params) { 839 assert.strictEqual(params, 'x=y%3Fz'); 840 } 841 } 842 }); 843 var myRouter = new MyRouter; 844 Backbone.history.navigate('path?x=y%3Fz', true); 845 }); 846 847 QUnit.test('Navigate to a hash url.', function(assert) { 848 assert.expect(1); 849 Backbone.history.stop(); 850 Backbone.history = _.extend(new Backbone.History, {location: location}); 851 Backbone.history.start({pushState: true}); 852 var MyRouter = Backbone.Router.extend({ 853 routes: { 854 path: function(params) { 855 assert.strictEqual(params, 'x=y'); 856 } 857 } 858 }); 859 var myRouter = new MyRouter; 860 location.replace('http://example.com/path?x=y#hash'); 861 Backbone.history.checkUrl(); 862 }); 863 864 QUnit.test('#navigate to a hash url.', function(assert) { 865 assert.expect(1); 866 Backbone.history.stop(); 867 Backbone.history = _.extend(new Backbone.History, {location: location}); 868 Backbone.history.start({pushState: true}); 869 var MyRouter = Backbone.Router.extend({ 870 routes: { 871 path: function(params) { 872 assert.strictEqual(params, 'x=y'); 873 } 874 } 875 }); 876 var myRouter = new MyRouter; 877 Backbone.history.navigate('path?x=y#hash', true); 878 }); 879 880 QUnit.test('unicode pathname', function(assert) { 881 assert.expect(1); 882 location.replace('http://example.com/myyjä'); 883 Backbone.history.stop(); 884 Backbone.history = _.extend(new Backbone.History, {location: location}); 885 var MyRouter = Backbone.Router.extend({ 886 routes: { 887 myyjä: function() { 888 assert.ok(true); 889 } 890 } 891 }); 892 new MyRouter; 893 Backbone.history.start({pushState: true}); 894 }); 895 896 QUnit.test('unicode pathname with % in a parameter', function(assert) { 897 assert.expect(1); 898 location.replace('http://example.com/myyjä/foo%20%25%3F%2f%40%25%20bar'); 899 location.pathname = '/myyj%C3%A4/foo%20%25%3F%2f%40%25%20bar'; 900 Backbone.history.stop(); 901 Backbone.history = _.extend(new Backbone.History, {location: location}); 902 var MyRouter = Backbone.Router.extend({ 903 routes: { 904 'myyjä/:query': function(query) { 905 assert.strictEqual(query, 'foo %?/@% bar'); 906 } 907 } 908 }); 909 new MyRouter; 910 Backbone.history.start({pushState: true}); 911 }); 912 913 QUnit.test('newline in route', function(assert) { 914 assert.expect(1); 915 location.replace('http://example.com/stuff%0Anonsense?param=foo%0Abar'); 916 Backbone.history.stop(); 917 Backbone.history = _.extend(new Backbone.History, {location: location}); 918 var MyRouter = Backbone.Router.extend({ 919 routes: { 920 'stuff\nnonsense': function() { 921 assert.ok(true); 922 } 923 } 924 }); 925 new MyRouter; 926 Backbone.history.start({pushState: true}); 927 }); 928 929 QUnit.test('Router#execute receives callback, args, name.', function(assert) { 930 assert.expect(3); 931 location.replace('http://example.com#foo/123/bar?x=y'); 932 Backbone.history.stop(); 933 Backbone.history = _.extend(new Backbone.History, {location: location}); 934 var MyRouter = Backbone.Router.extend({ 935 routes: {'foo/:id/bar': 'foo'}, 936 foo: function() {}, 937 execute: function(callback, args, name) { 938 assert.strictEqual(callback, this.foo); 939 assert.deepEqual(args, ['123', 'x=y']); 940 assert.strictEqual(name, 'foo'); 941 } 942 }); 943 var myRouter = new MyRouter; 944 Backbone.history.start(); 945 }); 946 947 QUnit.test('pushState to hashChange with only search params.', function(assert) { 948 assert.expect(1); 949 Backbone.history.stop(); 950 location.replace('http://example.com?a=b'); 951 location.replace = function(url) { 952 assert.strictEqual(url, '/#?a=b'); 953 }; 954 Backbone.history = _.extend(new Backbone.History, { 955 location: location, 956 history: null 957 }); 958 Backbone.history.start({pushState: true}); 959 }); 960 961 QUnit.test('#3123 - History#navigate decodes before comparison.', function(assert) { 962 assert.expect(1); 963 Backbone.history.stop(); 964 location.replace('http://example.com/shop/search?keyword=short%20dress'); 965 Backbone.history = _.extend(new Backbone.History, { 966 location: location, 967 history: { 968 pushState: function() { assert.ok(false); }, 969 replaceState: function() { assert.ok(false); } 970 } 971 }); 972 Backbone.history.start({pushState: true}); 973 Backbone.history.navigate('shop/search?keyword=short%20dress', true); 974 assert.strictEqual(Backbone.history.fragment, 'shop/search?keyword=short dress'); 975 }); 976 977 QUnit.test('#3175 - Urls in the params', function(assert) { 978 assert.expect(1); 979 Backbone.history.stop(); 980 location.replace('http://example.com#login?a=value&backUrl=https%3A%2F%2Fwww.msn.com%2Fidp%2Fidpdemo%3Fspid%3Dspdemo%26target%3Db'); 981 Backbone.history = _.extend(new Backbone.History, {location: location}); 982 var myRouter = new Backbone.Router; 983 myRouter.route('login', function(params) { 984 assert.strictEqual(params, 'a=value&backUrl=https%3A%2F%2Fwww.msn.com%2Fidp%2Fidpdemo%3Fspid%3Dspdemo%26target%3Db'); 985 }); 986 Backbone.history.start(); 987 }); 988 989 QUnit.test('#3358 - pushState to hashChange transition with search params', function(assert) { 990 assert.expect(1); 991 Backbone.history.stop(); 992 location.replace('http://example.com/root?foo=bar'); 993 location.replace = function(url) { 994 assert.strictEqual(url, '/root#?foo=bar'); 995 }; 996 Backbone.history = _.extend(new Backbone.History, { 997 location: location, 998 history: { 999 pushState: undefined, 1000 replaceState: undefined 1001 } 1002 }); 1003 Backbone.history.start({root: '/root', pushState: true}); 1004 }); 1005 1006 QUnit.test('Paths that don\'t match the root should not match no root', function(assert) { 1007 assert.expect(0); 1008 location.replace('http://example.com/foo'); 1009 Backbone.history.stop(); 1010 Backbone.history = _.extend(new Backbone.History, {location: location}); 1011 var MyRouter = Backbone.Router.extend({ 1012 routes: { 1013 foo: function() { 1014 assert.ok(false, 'should not match unless root matches'); 1015 } 1016 } 1017 }); 1018 var myRouter = new MyRouter; 1019 Backbone.history.start({root: 'root', pushState: true}); 1020 }); 1021 1022 QUnit.test('Paths that don\'t match the root should not match roots of the same length', function(assert) { 1023 assert.expect(0); 1024 location.replace('http://example.com/xxxx/foo'); 1025 Backbone.history.stop(); 1026 Backbone.history = _.extend(new Backbone.History, {location: location}); 1027 var MyRouter = Backbone.Router.extend({ 1028 routes: { 1029 foo: function() { 1030 assert.ok(false, 'should not match unless root matches'); 1031 } 1032 } 1033 }); 1034 var myRouter = new MyRouter; 1035 Backbone.history.start({root: 'root', pushState: true}); 1036 }); 1037 1038 QUnit.test('roots with regex characters', function(assert) { 1039 assert.expect(1); 1040 location.replace('http://example.com/x+y.z/foo'); 1041 Backbone.history.stop(); 1042 Backbone.history = _.extend(new Backbone.History, {location: location}); 1043 var MyRouter = Backbone.Router.extend({ 1044 routes: {foo: function() { assert.ok(true); }} 1045 }); 1046 var myRouter = new MyRouter; 1047 Backbone.history.start({root: 'x+y.z', pushState: true}); 1048 }); 1049 1050 QUnit.test('roots with unicode characters', function(assert) { 1051 assert.expect(1); 1052 location.replace('http://example.com/®ooτ/foo'); 1053 Backbone.history.stop(); 1054 Backbone.history = _.extend(new Backbone.History, {location: location}); 1055 var MyRouter = Backbone.Router.extend({ 1056 routes: {foo: function() { assert.ok(true); }} 1057 }); 1058 var myRouter = new MyRouter; 1059 Backbone.history.start({root: '®ooτ', pushState: true}); 1060 }); 1061 1062 QUnit.test('roots without slash', function(assert) { 1063 assert.expect(1); 1064 location.replace('http://example.com/®ooτ'); 1065 Backbone.history.stop(); 1066 Backbone.history = _.extend(new Backbone.History, {location: location}); 1067 var MyRouter = Backbone.Router.extend({ 1068 routes: {'': function() { assert.ok(true); }} 1069 }); 1070 var myRouter = new MyRouter; 1071 Backbone.history.start({root: '®ooτ', pushState: true}); 1072 }); 1073 1074 QUnit.test('#4025 - navigate updates URL hash as is', function(assert) { 1075 assert.expect(1); 1076 var route = 'search/has%20space'; 1077 Backbone.history.navigate(route); 1078 assert.strictEqual(location.hash, '#' + route); 1079 }); 1080 1081})(QUnit); 1082