Skip to content

Commit e31453d

Browse files
committed
refactor($browser): more test coverage around history.state manipulation
Check that pushState is not invoked if $browser.url() and $browser.state() is passed to $browser.url setter. Also, a minor refactor in $browser.url code and $browser specs. Refs angular#9587
1 parent 28133cb commit e31453d

File tree

2 files changed

+29
-14
lines changed

2 files changed

+29
-14
lines changed

src/ng/browser.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,12 @@ function Browser(window, document, $log, $sniffer) {
165165

166166
// setter
167167
if (url) {
168+
var sameState = lastHistoryState === state;
169+
168170
// Don't change anything if previous and current URLs and states match. This also prevents
169171
// IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
170172
// See https://github.com/angular/angular.js/commit/ffb2701
171-
if (lastBrowserUrl === url && (!$sniffer.history || cachedState === state)) {
173+
if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
172174
return;
173175
}
174176
var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
@@ -178,9 +180,10 @@ function Browser(window, document, $log, $sniffer) {
178180
// due to a bug in IE10/IE11 which leads
179181
// to not firing a `hashchange` nor `popstate` event
180182
// in some cases (see #9143).
181-
if ($sniffer.history && (!sameBase || cachedState !== state)) {
183+
if ($sniffer.history && (!sameBase || !sameState)) {
182184
history[replace ? 'replaceState' : 'pushState'](state, '', url);
183185
cacheState();
186+
// Do the assignment again so that those two variables are referentially identical.
184187
lastHistoryState = cachedState;
185188
} else {
186189
if (!sameBase) {

test/ng/browserSpecs.js

+24-12
Original file line numberDiff line numberDiff line change
@@ -641,11 +641,9 @@ describe('browser', function() {
641641
});
642642

643643
describe('url (when state passed)', function() {
644-
var currentHref;
644+
var currentHref, pushState, replaceState, locationReplace;
645645

646646
beforeEach(function() {
647-
sniffer = {history: true};
648-
currentHref = fakeWindow.location.href;
649647
});
650648

651649
describe('in IE', runTests({msie: true}));
@@ -654,7 +652,14 @@ describe('browser', function() {
654652
function runTests(options) {
655653
return function() {
656654
beforeEach(function() {
655+
sniffer = {history: true};
656+
657657
fakeWindow = new MockWindow({msie: options.msie});
658+
currentHref = fakeWindow.location.href;
659+
pushState = spyOn(fakeWindow.history, 'pushState').andCallThrough();
660+
replaceState = spyOn(fakeWindow.history, 'replaceState').andCallThrough();
661+
locationReplace = spyOn(fakeWindow.location, 'replace').andCallThrough();
662+
658663
browser = new Browser(fakeWindow, fakeDocument, fakeLog, sniffer);
659664
browser.onUrlChange(function() {});
660665
});
@@ -703,20 +708,27 @@ describe('browser', function() {
703708
expect(fakeWindow.history.state).toEqual({prop: 'val3'});
704709
});
705710

706-
it('should do pushState with the same URL and null state', function() {
707-
fakeWindow.history.state = {prop: 'val1'};
711+
it('should do pushState with the same URL and deep equal but referentially different state', function() {
712+
fakeWindow.history.state = {prop: 'val'};
708713
fakeWindow.fire('popstate');
714+
expect(historyEntriesLength).toBe(1);
709715

710-
browser.url(currentHref, false, null);
711-
expect(fakeWindow.history.state).toEqual(null);
716+
browser.url(currentHref, false, {prop: 'val'});
717+
expect(fakeWindow.history.state).toEqual({prop: 'val'});
718+
expect(historyEntriesLength).toBe(2);
712719
});
713720

714-
it('should do pushState with the same URL and the same non-null state', function() {
715-
fakeWindow.history.state = null;
716-
fakeWindow.fire('popstate');
721+
it('should not do pushState with the same URL and state from $browser.state()', function() {
722+
browser.url(currentHref, false, {prop: 'val'});
717723

718-
browser.url(currentHref, false, {prop: 'val2'});
719-
expect(fakeWindow.history.state).toEqual({prop: 'val2'});
724+
pushState.reset();
725+
replaceState.reset();
726+
locationReplace.reset();
727+
728+
browser.url(currentHref, false, browser.state());
729+
expect(pushState).not.toHaveBeenCalled();
730+
expect(replaceState).not.toHaveBeenCalled();
731+
expect(locationReplace).not.toHaveBeenCalled();
720732
});
721733
};
722734
}

0 commit comments

Comments
 (0)