Skip to content

Commit 930af9d

Browse files
atscottdylhunn
authored andcommitted
fix(common): Fix MockPlatformLocation events and missing onPopState implementation (#48113)
The MockPlatformLocation forward, back, and historyGo methods should trigger a popstate event. Additionally, these events should just be synchronous since that's what the majority of the major browsers do. Lastly, onPopState should be implemented the same way as onHashChange. PR Close #48113
1 parent 7e3dad1 commit 930af9d

File tree

1 file changed

+22
-14
lines changed

1 file changed

+22
-14
lines changed

packages/common/testing/src/mock_platform_location.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ export const MOCK_PLATFORM_LOCATION_CONFIG =
105105
export 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

Comments
 (0)