@@ -12,12 +12,20 @@ function MockWindow(options) {
12
12
var events = { } ;
13
13
var timeouts = this . timeouts = [ ] ;
14
14
var locationHref = 'http://server/' ;
15
+ var committedHref = 'http://server/' ;
15
16
var mockWindow = this ;
16
17
var msie = options . msie ;
17
18
var ieState ;
18
19
19
20
historyEntriesLength = 1 ;
20
21
22
+ function replaceHash ( href , hash ) {
23
+ // replace the hash with the new one (stripping off a leading hash if there is one)
24
+ // See hash setter spec: https://url.spec.whatwg.org/#urlutils-and-urlutilsreadonly-members
25
+ return stripHash ( href ) + '#' + hash . replace ( / ^ # / , '' ) ;
26
+ }
27
+
28
+
21
29
this . setTimeout = function ( fn ) {
22
30
return timeouts . push ( fn ) - 1 ;
23
31
} ;
@@ -46,24 +54,28 @@ function MockWindow(options) {
46
54
47
55
this . location = {
48
56
get href ( ) {
49
- return locationHref ;
57
+ return committedHref ;
50
58
} ,
51
59
set href ( value ) {
52
60
locationHref = value ;
53
61
mockWindow . history . state = null ;
54
62
historyEntriesLength ++ ;
63
+ if ( ! options . updateAsync ) this . flushHref ( ) ;
55
64
} ,
56
65
get hash ( ) {
57
- return getHash ( locationHref ) ;
66
+ return getHash ( committedHref ) ;
58
67
} ,
59
68
set hash ( value ) {
60
- // replace the hash with the new one (stripping off a leading hash if there is one)
61
- // See hash setter spec: https://url.spec.whatwg.org/#urlutils-and-urlutilsreadonly-members
62
- locationHref = stripHash ( locationHref ) + '#' + value . replace ( / ^ # / , '' ) ;
69
+ locationHref = replaceHash ( locationHref , value ) ;
70
+ if ( ! options . updateAsync ) this . flushHref ( ) ;
63
71
} ,
64
72
replace : function ( url ) {
65
73
locationHref = url ;
66
74
mockWindow . history . state = null ;
75
+ if ( ! options . updateAsync ) this . flushHref ( ) ;
76
+ } ,
77
+ flushHref : function ( ) {
78
+ committedHref = locationHref ;
67
79
}
68
80
} ;
69
81
@@ -132,7 +144,7 @@ describe('browser', function() {
132
144
133
145
logs = { log :[ ] , warn :[ ] , info :[ ] , error :[ ] } ;
134
146
135
- var fakeLog = { log : function ( ) { logs . log . push ( slice . call ( arguments ) ) ; } ,
147
+ fakeLog = { log : function ( ) { logs . log . push ( slice . call ( arguments ) ) ; } ,
136
148
warn : function ( ) { logs . warn . push ( slice . call ( arguments ) ) ; } ,
137
149
info : function ( ) { logs . info . push ( slice . call ( arguments ) ) ; } ,
138
150
error : function ( ) { logs . error . push ( slice . call ( arguments ) ) ; } } ;
@@ -703,7 +715,11 @@ describe('browser', function() {
703
715
describe ( 'integration tests with $location' , function ( ) {
704
716
705
717
function setup ( options ) {
718
+ fakeWindow = new MockWindow ( options ) ;
719
+ browser = new Browser ( fakeWindow , fakeDocument , fakeLog , sniffer ) ;
720
+
706
721
module ( function ( $provide , $locationProvider ) {
722
+
707
723
spyOn ( fakeWindow . history , 'pushState' ) . andCallFake ( function ( stateObj , title , newUrl ) {
708
724
fakeWindow . location . href = newUrl ;
709
725
} ) ;
@@ -827,6 +843,32 @@ describe('browser', function() {
827
843
} ) ;
828
844
829
845
} ) ;
846
+
847
+ // issue #12241
848
+ it ( 'should not infinite digest if the browser does not synchronously update the location properties' , function ( ) {
849
+ setup ( {
850
+ history : true ,
851
+ html5Mode : true ,
852
+ updateAsync : true // Simulate a browser that doesn't update the href synchronously
853
+ } ) ;
854
+
855
+ inject ( function ( $location , $rootScope ) {
856
+
857
+ // Change the hash within Angular and check that we don't infinitely digest
858
+ $location . hash ( 'newHash' ) ;
859
+ expect ( function ( ) { $rootScope . $digest ( ) ; } ) . not . toThrow ( ) ;
860
+ expect ( $location . absUrl ( ) ) . toEqual ( 'http://server/#newHash' ) ;
861
+
862
+ // Now change the hash from outside Angular and check that $location updates correctly
863
+ fakeWindow . location . hash = '#otherHash' ;
864
+
865
+ // simulate next tick - since this browser doesn't update synchronously
866
+ fakeWindow . location . flushHref ( ) ;
867
+ fakeWindow . fire ( 'hashchange' ) ;
868
+
869
+ expect ( $location . absUrl ( ) ) . toEqual ( 'http://server/#otherHash' ) ;
870
+ } ) ;
871
+ } ) ;
830
872
} ) ;
831
873
832
874
describe ( 'integration test with $rootScope' , function ( ) {
0 commit comments