@@ -105,6 +105,7 @@ export const MOCK_PLATFORM_LOCATION_CONFIG =
105105export class MockPlatformLocation implements PlatformLocation {
106106 private baseHref : string = '' ;
107107 private hashUpdate = new Subject < LocationChangeEvent > ( ) ;
108+ private popStateSubject = new Subject < LocationChangeEvent > ( ) ;
108109 private urlChangeIndex : number = 0 ;
109110 private urlChanges : {
110111 hostname : string ,
@@ -155,9 +156,8 @@ export class MockPlatformLocation implements PlatformLocation {
155156 }
156157
157158 onPopState ( fn : LocationChangeListener ) : VoidFunction {
158- // No-op: a state stack is not implemented, so
159- // no events will ever come.
160- return ( ) => { } ;
159+ const subscription = this . popStateSubject . subscribe ( fn ) ;
160+ return ( ) => subscription . unsubscribe ( ) ;
161161 }
162162
163163 onHashChange ( fn : LocationChangeListener ) : VoidFunction {
@@ -204,7 +204,7 @@ export class MockPlatformLocation implements PlatformLocation {
204204 if ( this . urlChangeIndex < this . urlChanges . length ) {
205205 this . urlChangeIndex ++ ;
206206 }
207- this . scheduleHashUpdate ( oldHash , oldUrl ) ;
207+ this . emitEvents ( oldHash , oldUrl ) ;
208208 }
209209
210210 back ( ) : void {
@@ -213,7 +213,7 @@ export class MockPlatformLocation implements PlatformLocation {
213213 if ( this . urlChangeIndex > 0 ) {
214214 this . urlChangeIndex -- ;
215215 }
216- this . scheduleHashUpdate ( oldHash , oldUrl ) ;
216+ this . emitEvents ( oldHash , oldUrl ) ;
217217 }
218218
219219 historyGo ( relativePosition : number = 0 ) : void {
@@ -223,22 +223,30 @@ export class MockPlatformLocation implements PlatformLocation {
223223 if ( nextPageIndex >= 0 && nextPageIndex < this . urlChanges . length ) {
224224 this . urlChangeIndex = nextPageIndex ;
225225 }
226- this . scheduleHashUpdate ( oldHash , oldUrl ) ;
226+ this . emitEvents ( oldHash , oldUrl ) ;
227227 }
228228
229229 getState ( ) : unknown {
230230 return this . state ;
231231 }
232232
233- private scheduleHashUpdate ( oldHash : string , oldUrl : string ) {
233+ /**
234+ * Browsers are inconsistent in when they fire events and perform the state updates
235+ * The most easiest thing to do in our mock is synchronous and that happens to match
236+ * Firefox and Chrome, at least somewhat closely
237+ *
238+ * https://github.com/WICG/navigation-api#watching-for-navigations
239+ * https://docs.google.com/document/d/1Pdve-DJ1JCGilj9Yqf5HxRJyBKSel5owgOvUJqTauwU/edit#heading=h.3ye4v71wsz94
240+ * popstate is always sent before hashchange:
241+ * https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event#when_popstate_is_sent
242+ */
243+ private emitEvents ( oldHash : string , oldUrl : string ) {
244+ this . popStateSubject . next (
245+ { type : 'popstate' , state : this . getState ( ) , oldUrl, newUrl : this . url } as
246+ LocationChangeEvent ) ;
234247 if ( oldHash !== this . hash ) {
235- scheduleMicroTask (
236- ( ) => this . hashUpdate . next (
237- { type : 'hashchange' , state : null , oldUrl, newUrl : this . url } as LocationChangeEvent ) ) ;
248+ this . hashUpdate . next (
249+ { type : 'hashchange' , state : null , oldUrl, newUrl : this . url } as LocationChangeEvent ) ;
238250 }
239251 }
240252}
241-
242- export function scheduleMicroTask ( cb : ( ) => any ) {
243- Promise . resolve ( ) . then ( cb ) ;
244- }
0 commit comments