diff --git a/modules/__tests__/History-test.js b/modules/__tests__/History-test.js index 831d17cb10..c806162410 100644 --- a/modules/__tests__/History-test.js +++ b/modules/__tests__/History-test.js @@ -14,8 +14,9 @@ describe('History', function () { }); describe('after navigating to a route', function () { + var location; beforeEach(function () { - TestLocation.history = [ '/foo' ]; + location = new TestLocation([ '/foo' ]); }); it('has length 2', function (done) { @@ -26,7 +27,7 @@ describe('History', function () { var count = 0; - var router = Router.run(routes, TestLocation, function (Handler) { + var router = Router.run(routes, location, function (Handler) { count += 1; if (count === 2) { @@ -47,7 +48,7 @@ describe('History', function () { var count = 0; - var router = Router.run(routes, TestLocation, function (Handler) { + var router = Router.run(routes, location, function (Handler) { count += 1; if (count === 2) { diff --git a/modules/__tests__/Router-test.js b/modules/__tests__/Router-test.js index f5accab3b6..9ecc27e98f 100644 --- a/modules/__tests__/Router-test.js +++ b/modules/__tests__/Router-test.js @@ -37,7 +37,7 @@ describe('Router', function () { describe('asynchronous willTransitionTo', function () { it('waits', function (done) { - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var div = document.createElement('div'); var steps = []; @@ -60,7 +60,7 @@ describe('Router', function () { router = Router.create({ routes: routes, - location: TestLocation + location: location }); router.run(function (Handler) { @@ -71,7 +71,7 @@ describe('Router', function () { }); it('stops waiting on location.pop', function (done) { - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var div = document.createElement('div'); var steps = []; @@ -84,7 +84,7 @@ describe('Router', function () { setTimeout(function () { expect(div.innerHTML).toMatch(/Bar/); - TestLocation.pop(); + location.pop(); expect(div.innerHTML).toMatch(/Bar/); }, Async.delay / 2); @@ -96,7 +96,7 @@ describe('Router', function () { router = Router.create({ routes: routes, - location: TestLocation + location: location }); router.run(function (Handler) { @@ -107,7 +107,7 @@ describe('Router', function () { }); it('stops waiting on router.transitionTo', function (done) { - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var div = document.createElement('div'); var steps = []; @@ -128,7 +128,7 @@ describe('Router', function () { steps.push(function () { setTimeout(function () { expect(div.innerHTML).toMatch(/Foo/); - TestLocation.pop(); + location.pop(); }, Async.delay / 2 + 10); }); @@ -139,7 +139,7 @@ describe('Router', function () { router = Router.create({ routes: routes, - location: TestLocation + location: location }); router.run(function (Handler) { @@ -150,7 +150,7 @@ describe('Router', function () { }); it('stops waiting on router.replaceWith', function (done) { - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var div = document.createElement('div'); var steps = []; @@ -177,7 +177,7 @@ describe('Router', function () { router = Router.create({ routes: routes, - location: TestLocation + location: location }); router.run(function (Handler) { @@ -202,7 +202,7 @@ describe('Router', function () { } }); - TestLocation.history = [ '/foo' ]; + var location = new TestLocation([ '/foo' ]); var routes = [ , , @@ -214,7 +214,7 @@ describe('Router', function () { var steps = []; var router = Router.create({ routes: routes, - location: TestLocation + location: location }); steps.push(function () { @@ -250,11 +250,11 @@ describe('Router', function () { describe('transition.redirect', function () { it('redirects synchronously in willTransitionTo', function (done) { - TestLocation.history = [ '/redirect' ]; + var location = new TestLocation([ '/redirect' ]); var div = document.createElement('div'); - Router.run(routes, TestLocation, function (Handler) { + Router.run(routes, location, function (Handler) { React.render(, div, function () { expect(div.innerHTML).toMatch(/Foo/); done(); @@ -263,7 +263,7 @@ describe('Router', function () { }); it('redirects asynchronously in willTransitionTo', function (done) { - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var div = document.createElement('div'); var steps = []; @@ -287,7 +287,7 @@ describe('Router', function () { router = Router.create({ routes: routes, - location: TestLocation + location: location }); router.run(function (Handler) { @@ -298,7 +298,7 @@ describe('Router', function () { }); it('cancels redirecting asynchronously in willTransitionTo on location.pop', function (done) { - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var div = document.createElement('div'); var steps = []; @@ -311,7 +311,7 @@ describe('Router', function () { setTimeout(function () { expect(div.innerHTML).toMatch(/Bar/); - TestLocation.pop(); + location.pop(); expect(div.innerHTML).toMatch(/Bar/); }, RedirectToFooAsync.delay / 2); @@ -323,7 +323,7 @@ describe('Router', function () { router = Router.create({ routes: routes, - location: TestLocation + location: location }); router.run(function (Handler) { @@ -334,7 +334,7 @@ describe('Router', function () { }); it('cancels redirecting asynchronously in willTransitionTo on router.transitionTo', function (done) { - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var div = document.createElement('div'); var steps = []; @@ -355,7 +355,7 @@ describe('Router', function () { steps.push(function () { setTimeout(function () { expect(div.innerHTML).toMatch(/Baz/); - TestLocation.pop(); + location.pop(); }, RedirectToFooAsync.delay / 2 + 10); }); @@ -366,7 +366,7 @@ describe('Router', function () { router = Router.create({ routes: routes, - location: TestLocation + location: location }); router.run(function (Handler) { @@ -377,7 +377,7 @@ describe('Router', function () { }); it('cancels redirecting asynchronously in willTransitionTo on router.replaceWith', function (done) { - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var div = document.createElement('div'); var steps = []; @@ -404,7 +404,7 @@ describe('Router', function () { router = Router.create({ routes: routes, - location: TestLocation + location: location }); router.run(function (Handler) { @@ -417,22 +417,22 @@ describe('Router', function () { describe('transition.abort', function () { it('aborts synchronously in willTransitionTo', function (done) { - TestLocation.history = [ '/foo' ]; + var location = new TestLocation([ '/foo' ]); var div = document.createElement('div'); - Router.run(routes, TestLocation, function (Handler) { + Router.run(routes, location, function (Handler) { React.render(, div, function () { - TestLocation.push('/abort'); + location.push('/abort'); expect(div.innerHTML).toMatch(/Foo/); - expect(TestLocation.getCurrentPath()).toEqual('/foo'); + expect(location.getCurrentPath()).toEqual('/foo'); done(); }); }); }); it('aborts asynchronously in willTransitionTo', function (done) { - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var div = document.createElement('div'); var steps = []; @@ -455,7 +455,7 @@ describe('Router', function () { router = Router.create({ routes: routes, - location: TestLocation + location: location }); router.run(function (Handler) { @@ -466,7 +466,7 @@ describe('Router', function () { }); it('ignores aborting asynchronously in willTransitionTo on location.pop', function (done) { - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var div = document.createElement('div'); var steps = []; @@ -479,7 +479,7 @@ describe('Router', function () { setTimeout(function () { expect(div.innerHTML).toMatch(/Bar/); - TestLocation.pop(); + location.pop(); expect(div.innerHTML).toMatch(/Bar/); }, Async.delay / 2); @@ -491,7 +491,7 @@ describe('Router', function () { router = Router.create({ routes: routes, - location: TestLocation + location: location }); router.run(function (Handler) { @@ -502,7 +502,7 @@ describe('Router', function () { }); it('ignores aborting asynchronously in willTransitionTo on router.transitionTo', function (done) { - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var div = document.createElement('div'); var steps = []; @@ -523,7 +523,7 @@ describe('Router', function () { steps.push(function () { setTimeout(function () { expect(div.innerHTML).toMatch(/Foo/); - TestLocation.pop(); + location.pop(); }, Async.delay / 2 + 10); }); @@ -534,7 +534,7 @@ describe('Router', function () { router = Router.create({ routes: routes, - location: TestLocation + location: location }); router.run(function (Handler) { @@ -545,7 +545,7 @@ describe('Router', function () { }); it('ignores aborting asynchronously in willTransitionTo on router.replaceWith', function (done) { - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var div = document.createElement('div'); var steps = []; @@ -572,7 +572,7 @@ describe('Router', function () { router = Router.create({ routes: routes, - location: TestLocation + location: location }); router.run(function (Handler) { @@ -596,7 +596,7 @@ describe('Router', function () { } }); - TestLocation.history = [ '/foo' ]; + var location = new TestLocation([ '/foo' ]); var routes = [ , , @@ -607,7 +607,7 @@ describe('Router', function () { var steps = []; var router = Router.create({ routes: routes, - location: TestLocation + location: location }); steps.push(function () { @@ -621,7 +621,7 @@ describe('Router', function () { steps.push(function () { setTimeout(function () { expect(div.innerHTML).toMatch(/Bar/); - expect(TestLocation.history).toEqual(['/foo', '/bar']); + expect(location.history).toEqual(['/foo', '/bar']); done(); }, Async.delay + 10); }); @@ -693,14 +693,14 @@ describe('Router', function () { ); - TestLocation.history = [ '/spoon?filter=first' ]; + var location = new TestLocation([ '/spoon?filter=first' ]); var div = document.createElement('div'); - Router.run(routes, TestLocation, function (Handler, state) { + Router.run(routes, location, function (Handler, state) { React.render(, div); }); - TestLocation.push('/spoon?filter=second'); + location.push('/spoon?filter=second'); }); }); @@ -728,11 +728,11 @@ describe('Router', function () { ); - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); - Router.run(routes, TestLocation, function (Handler, state) { + Router.run(routes, location, function (Handler, state) { React.render(, div, function () { - TestLocation.push('/baz'); + location.push('/baz'); }); }); }); @@ -773,16 +773,16 @@ describe('Router', function () { ); - TestLocation.history = [ '/bar' ]; + var location = new TestLocation([ '/bar' ]); var steps = []; steps.push(function () { - TestLocation.push('/foo'); + location.push('/foo'); }); steps.push(function () { - TestLocation.push('/bar'); + location.push('/bar'); }); steps.push(function () { @@ -790,7 +790,7 @@ describe('Router', function () { done(); }); - Router.run(routes, TestLocation, function (Handler, state) { + Router.run(routes, location, function (Handler, state) { // Calling render on the handler twice should be allowed React.render(, div); @@ -882,7 +882,7 @@ describe('Router.run', function () { }); it('does not blow away the previous HTML', function (done) { - TestLocation.history = [ '/foo' ]; + var location = new TestLocation([ '/foo' ]); var routes = ( @@ -895,7 +895,7 @@ describe('Router.run', function () { steps.push(function () { expect(div.innerHTML).toMatch(/foo/); div.querySelector('h1').innerHTML = 'lol i changed you'; - TestLocation.push('/bar'); + location.push('/bar'); }); steps.push(function () { @@ -904,7 +904,7 @@ describe('Router.run', function () { done(); }); - Router.run(routes, TestLocation, function (Handler, state) { + Router.run(routes, location, function (Handler, state) { React.render(, div, function () { steps.shift()(); }); @@ -937,9 +937,9 @@ describe('Router.run', function () { ]; describe('when a page is scrolled', function () { - var position, div, renderCount; + var position, div, renderCount, location; beforeEach(function (done) { - TestLocation.history = [ '/one' ]; + location = new TestLocation([ '/one' ]); div = document.createElement('div'); document.body.appendChild(div); @@ -948,7 +948,7 @@ describe('Router.run', function () { Router.create({ routes: routes, - location: TestLocation, + location: location, scrollBehavior: ScrollToTopBehavior }).run(function (Handler) { React.render(, div, function () { @@ -973,7 +973,7 @@ describe('Router.run', function () { describe('navigating to a new page', function () { beforeEach(function () { - TestLocation.push('/two'); + location.push('/two'); }); it('resets the scroll position', function () { @@ -982,7 +982,7 @@ describe('Router.run', function () { describe('then returning to the previous page', function () { beforeEach(function () { - TestLocation.pop(); + location.pop(); }); it('resets the scroll position', function () { @@ -1006,16 +1006,16 @@ describe('Router.run', function () { ]; describe('when a page is scrolled', function () { - var position, div, renderCount; + var position, div, renderCount, location; beforeEach(function (done) { - TestLocation.history = [ '/one' ]; + location = new TestLocation([ '/one' ]); div = document.createElement('div'); document.body.appendChild(div); renderCount = 0; - Router.run(routes, TestLocation, function (Handler) { + Router.run(routes, location, function (Handler) { React.render(, div, function () { if (renderCount === 0) { position = { x: 20, y: 50 }; @@ -1038,7 +1038,7 @@ describe('Router.run', function () { describe('navigating to a new page', function () { beforeEach(function () { - TestLocation.push('/two'); + location.push('/two'); }); it('resets the scroll position', function () { @@ -1047,7 +1047,7 @@ describe('Router.run', function () { describe('then returning to the previous page', function () { beforeEach(function () { - TestLocation.pop(); + location.pop(); }); it('remembers the scroll position', function () { @@ -1071,9 +1071,9 @@ describe('Router.run', function () { ); - var div, didUpdateScroll; + var div, didUpdateScroll, location; beforeEach(function (done) { - TestLocation.history = [ '/feed' ]; + location = new TestLocation([ '/feed' ]); div = document.createElement('div'); document.body.appendChild(div); @@ -1086,7 +1086,7 @@ describe('Router.run', function () { Router.create({ routes: routes, - location: TestLocation, + location: location, scrollBehavior: MockScrollBehavior }).run(function (Handler) { React.render(, div, function () { @@ -1110,59 +1110,59 @@ describe('Router.run', function () { }); afterEach(function () { - TestLocation.pop(); + location.pop(); }); it('calls updateScroll when no ancestors ignore scroll', function () { - TestLocation.push('/about'); + location.push('/about'); expect(didUpdateScroll).toBe(true); }); it('calls updateScroll when no ancestors ignore scroll although source and target do', function () { - TestLocation.push('/search/foo'); + location.push('/search/foo'); expect(didUpdateScroll).toBe(true); }); it('calls updateScroll when route does not ignore scroll and only params change', function () { - TestLocation.replace('/users/3/posts'); + location.replace('/users/3/posts'); didUpdateScroll = false; - TestLocation.push('/users/5/posts'); + location.push('/users/5/posts'); expect(didUpdateScroll).toBe(true); }); it('calls updateScroll when route does not ignore scroll and both params and query change', function () { - TestLocation.replace('/users/3/posts'); + location.replace('/users/3/posts'); didUpdateScroll = false; - TestLocation.push('/users/5/posts?page=2'); + location.push('/users/5/posts?page=2'); expect(didUpdateScroll).toBe(true); }); it('does not call updateScroll when route does not ignore scroll but only query changes', function () { - TestLocation.replace('/users/3/posts'); + location.replace('/users/3/posts'); didUpdateScroll = false; - TestLocation.push('/users/3/posts?page=2'); + location.push('/users/3/posts?page=2'); expect(didUpdateScroll).toBe(false); }); it('does not call updateScroll when common ancestor ignores scroll', function () { - TestLocation.push('/discover'); + location.push('/discover'); expect(didUpdateScroll).toBe(false); }); it('does not call updateScroll when route ignores scroll', function () { - TestLocation.replace('/search/foo'); + location.replace('/search/foo'); didUpdateScroll = false; - TestLocation.push('/search/bar'); + location.push('/search/bar'); expect(didUpdateScroll).toBe(false); - TestLocation.replace('/search/bar?safe=0'); + location.replace('/search/bar?safe=0'); expect(didUpdateScroll).toBe(false); - TestLocation.replace('/search/whatever'); + location.replace('/search/whatever'); expect(didUpdateScroll).toBe(false); }); }); diff --git a/modules/__tests__/State-test.js b/modules/__tests__/State-test.js index 5fd607a392..e66b88767a 100644 --- a/modules/__tests__/State-test.js +++ b/modules/__tests__/State-test.js @@ -10,12 +10,14 @@ describe('State', function () { describe('when a route is active', function () { describe('and it has no params', function () { it('is active', function (done) { + var location = new TestLocation([ '/foo' ]); var div = document.createElement('div'); - TestLocation.history = ['/foo']; + var routes = ( ); - Router.run(routes, TestLocation, function (Handler) { + + Router.run(routes, location, function (Handler) { React.render(, div, function () { assert(this.isActive('foo')); done(); @@ -25,13 +27,13 @@ describe('State', function () { }); describe('and the right params are given', function () { - var component; + var component, location; var div = document.createElement('div'); var routes = ; beforeEach(function (done) { - TestLocation.history = ['/products/123/456?search=abc&limit=789']; - Router.run(routes, TestLocation, function (Handler) { + location = new TestLocation([ '/products/123/456?search=abc&limit=789' ]); + Router.run(routes, location, function (Handler) { React.render(, div, function () { component = this; done(); diff --git a/modules/components/__tests__/Link-test.js b/modules/components/__tests__/Link-test.js index 40e898199c..ffb8a90f79 100644 --- a/modules/components/__tests__/Link-test.js +++ b/modules/components/__tests__/Link-test.js @@ -24,9 +24,9 @@ describe('A Link', function () { ]; var div = document.createElement('div'); - TestLocation.history = [ '/link' ]; + var location = new TestLocation([ '/link' ]); - Router.run(routes, TestLocation, function (Handler) { + Router.run(routes, location, function (Handler) { React.render(, div, function () { var a = div.querySelector('a'); expect(a.getAttribute('href')).toEqual('/foo/baz?qux=quux'); @@ -60,7 +60,7 @@ describe('A Link', function () { ); var div = document.createElement('div'); - TestLocation.history = ['/foo']; + var location = new TestLocation([ '/foo' ]); var steps = []; function assertActive () { @@ -75,12 +75,12 @@ describe('A Link', function () { steps.push(function () { assertActive(); - TestLocation.push('/bar'); + location.push('/bar'); }); steps.push(function () { assertInactive(); - TestLocation.push('/foo'); + location.push('/foo'); }); steps.push(function () { @@ -88,7 +88,7 @@ describe('A Link', function () { done(); }); - Router.run(routes, TestLocation, function (Handler) { + Router.run(routes, location, function (Handler) { React.render(, div, function () { steps.shift()(); }); @@ -119,7 +119,7 @@ describe('A Link', function () { ); var div = document.createElement('div'); - TestLocation.history = ['/foo']; + var location = new TestLocation([ '/foo' ]); var steps = []; function assertActive () { @@ -134,12 +134,12 @@ describe('A Link', function () { steps.push(function () { assertActive(); - TestLocation.push('/bar'); + location.push('/bar'); }); steps.push(function () { assertInactive(); - TestLocation.push('/foo'); + location.push('/foo'); }); steps.push(function () { @@ -147,7 +147,7 @@ describe('A Link', function () { done(); }); - Router.run(routes, TestLocation, function (Handler) { + Router.run(routes, location, function (Handler) { React.render(, div, function () { steps.shift()(); }); @@ -173,9 +173,9 @@ describe('A Link', function () { ]; var div = document.createElement('div'); - TestLocation.history = [ '/link' ]; + var location = new TestLocation([ '/link' ]); - Router.run(routes, TestLocation, function (Handler) { + Router.run(routes, location, function (Handler) { React.render(, div, function () { click(div.querySelector('a')); }); @@ -184,7 +184,7 @@ describe('A Link', function () { it('transitions to the correct route', function (done) { var div = document.createElement('div'); - TestLocation.history = [ '/link' ]; + var location = new TestLocation([ '/link' ]); var LinkHandler = React.createClass({ handleClick: function () { @@ -212,7 +212,7 @@ describe('A Link', function () { done(); }); - Router.run(routes, TestLocation, function (Handler) { + Router.run(routes, location, function (Handler) { React.render(, div, function () { steps.shift()(); }); diff --git a/modules/components/__tests__/Redirect-test.js b/modules/components/__tests__/Redirect-test.js index ea72e96002..528ecc446b 100644 --- a/modules/components/__tests__/Redirect-test.js +++ b/modules/components/__tests__/Redirect-test.js @@ -9,7 +9,7 @@ var Route = require('../Route'); describe('Redirect', function () { it('defaults the path to "*"', function () { - TestLocation.history = [ '/kljfsdlfkjsdf' ]; + var location = new TestLocation([ '/kljfsdlfkjsdf' ]); var div = document.createElement('div'); var routes = [ @@ -17,7 +17,7 @@ describe('Redirect', function () { ]; - Router.run(routes, TestLocation, function (Handler) { + Router.run(routes, location, function (Handler) { React.render(, div); expect(div.innerHTML).toMatch(/Bar/); }); @@ -25,7 +25,7 @@ describe('Redirect', function () { describe('at the root of the config', function () { it('redirects', function () { - TestLocation.history = [ '/foo' ]; + var location = new TestLocation([ '/foo' ]); var div = document.createElement('div'); var routes = [ @@ -33,7 +33,7 @@ describe('Redirect', function () { ]; - Router.run(routes, TestLocation, function (Handler) { + Router.run(routes, location, function (Handler) { React.render(, div); expect(div.innerHTML).toMatch(/Bar/); }); @@ -42,7 +42,7 @@ describe('Redirect', function () { describe('nested deeply in the config', function () { it('redirects with absolute paths', function () { - TestLocation.history = [ '/foo/bar' ]; + var location = new TestLocation([ '/foo/bar' ]); var div = document.createElement('div'); var routes = ( @@ -54,14 +54,14 @@ describe('Redirect', function () { ); - Router.run(routes, TestLocation, function (Handler) { + Router.run(routes, location, function (Handler) { React.render(, div); expect(div.innerHTML).toMatch(/Bar/); }); }); it('redirects with relative paths', function () { - TestLocation.history = [ '/foo/bar' ]; + var location = new TestLocation([ '/foo/bar' ]); var div = document.createElement('div'); var routes = ( @@ -73,7 +73,7 @@ describe('Redirect', function () { ); - Router.run(routes, TestLocation, function (Handler) { + Router.run(routes, location, function (Handler) { React.render(, div); expect(div.innerHTML).toMatch(/Bar/); }); diff --git a/modules/components/__tests__/RouteHandler-test.js b/modules/components/__tests__/RouteHandler-test.js index 85457a93da..7eb9f20926 100644 --- a/modules/components/__tests__/RouteHandler-test.js +++ b/modules/components/__tests__/RouteHandler-test.js @@ -10,7 +10,7 @@ describe('RouteHandler', function () { it('uses the old handler until the top-level component is rendered again', function (done) { var updateComponentBeforeNextRender; - TestLocation.history = [ '/foo' ]; + var location = new TestLocation([ '/foo' ]); var Root = React.createClass({ componentDidMount: function () { @@ -42,7 +42,7 @@ describe('RouteHandler', function () { steps.push(function (Handler, state) { React.render(, div, function () { expect(div.innerHTML).toMatch(/Foo/); - TestLocation.push('/bar'); + location.push('/bar'); }); }); @@ -56,7 +56,7 @@ describe('RouteHandler', function () { }); }); - Router.run(routes, TestLocation, function () { + Router.run(routes, location, function () { steps.shift().apply(this, arguments); }); }); diff --git a/modules/locations/StaticLocation.js b/modules/locations/StaticLocation.js index 1f4f4e1c97..26acd8d795 100644 --- a/modules/locations/StaticLocation.js +++ b/modules/locations/StaticLocation.js @@ -4,6 +4,11 @@ function throwCannotModify() { invariant(false, 'You cannot modify a static location'); } +/** + * A location that only ever contains a single path. Useful in + * stateless environments like servers where there is no path history, + * only the path that was used in the request. + */ function StaticLocation(path) { this.path = path; } diff --git a/modules/locations/TestLocation.js b/modules/locations/TestLocation.js index 66bd6f0b74..057e721684 100644 --- a/modules/locations/TestLocation.js +++ b/modules/locations/TestLocation.js @@ -2,64 +2,65 @@ var invariant = require('react/lib/invariant'); var LocationActions = require('../actions/LocationActions'); var History = require('../History'); -var _listener; - -function notifyChange(type) { - if (_listener) - _listener({ path: TestLocation.getCurrentPath(), type: type }); +/** + * A location that is convenient for testing and does not require a DOM. + */ +function TestLocation(history) { + this.history = history || []; + this.listeners = []; + this._updateHistoryLength(); } -function updateHistoryLength() { - History.length = TestLocation.history.length; -} +TestLocation.prototype.needsDOM = false; -/** - * A location that is convenient for testing and does not - * require a DOM. You should manually setup TestLocation.history - * with the URL paths your test needs before it runs. - */ -var TestLocation = { +TestLocation.prototype._updateHistoryLength = function () { + History.length = this.history.length; +}; - history: [], +TestLocation.prototype._notifyChange = function (type) { + for (var i = 0, len = this.listeners.length; i < len; ++i) + this.listeners[i].call(this, { path: this.getCurrentPath(), type: type }); +}; - needsDOM: false, +TestLocation.prototype.addChangeListener = function (listener) { + this.listeners.push(listener); +}; - addChangeListener: function (listener) { - // TestLocation only ever supports a single listener at a time. - _listener = listener; - updateHistoryLength(); - }, +TestLocation.prototype.removeChangeListener = function (listener) { + this.listeners = this.listeners.filter(function (l) { + return l !== listener; + }); +}; - push: function (path) { - TestLocation.history.push(path); - updateHistoryLength(); - notifyChange(LocationActions.PUSH); - }, +TestLocation.prototype.push = function (path) { + this.history.push(path); + this._updateHistoryLength(); + this._notifyChange(LocationActions.PUSH); +}; - replace: function (path) { - invariant( - History.length, - 'You cannot replace the current path with no history' - ); +TestLocation.prototype.replace = function (path) { + invariant( + this.history.length, + 'You cannot replace the current path with no history' + ); - TestLocation.history[TestLocation.history.length - 1] = path; - notifyChange(LocationActions.REPLACE); - }, + this.history[this.history.length - 1] = path; - pop: function () { - TestLocation.history.pop(); - updateHistoryLength(); - notifyChange(LocationActions.POP); - }, + this._notifyChange(LocationActions.REPLACE); +}; - getCurrentPath: function () { - return TestLocation.history[TestLocation.history.length - 1]; - }, +TestLocation.prototype.pop = function () { + this.history.pop(); + this._updateHistoryLength(); + this._notifyChange(LocationActions.POP); +}; - toString: function () { - return ''; - } +TestLocation.prototype.getCurrentPath = function () { + return this.history[this.history.length - 1]; +}; +TestLocation.prototype.toString = function () { + return ''; }; module.exports = TestLocation;