Skip to content

Commit

Permalink
feat: 🎸 add location$ observable
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed May 2, 2020
1 parent ca9d50a commit ed69dd2
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 1 deletion.
1 change: 0 additions & 1 deletion src/__tests__/index.spec.ts

This file was deleted.

94 changes: 94 additions & 0 deletions src/location$.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {location$} from './location$';
const {window, _listeners} = require('./window');

type Listener = {event: string, listener: (...args: any) => void};

const listeners: Listener[] = _listeners;

jest.mock('./window', () => {
const listeners: Listener[] = [];
const removedListeners: Listener[] = [];
const location: Location = {
hash: '',
host: 'google.com',
href: 'http://google.com'
} as Location;
const wnd = {
location,
addEventListener: (event, listener) => {
listeners.push({event, listener});
},
removeEventListener: (event, listener) => {
removedListeners.push({event, listener});
},
};
return {
window: wnd,
_listeners: listeners,
_removedListeners: removedListeners,
};
});

test('can subscribe', () => {
location$.subscribe(() => {});
});

test('attaches 3 listeners', async () => {
expect(listeners.length).toBe(3);
});

test('fires on listener', async () => {
const spy = jest.fn();

location$.subscribe(spy);

expect(spy).toHaveBeenCalledTimes(0);

const event = new Event('popstate');
listeners[0].listener(event);

expect(spy).toHaveBeenCalledTimes(1);
expect(spy.mock.calls[0][0]).toBe(window.location);
});

test('emits again on location change', async () => {
const spy = jest.fn();

location$.subscribe(spy);

expect(spy).toHaveBeenCalledTimes(0);

const event1 = new Event('popstate');
listeners[0].listener(event1);

expect(spy).toHaveBeenCalledTimes(1);
expect(spy.mock.calls[0][0]).toBe(window.location);

window.location.hash = 'asdf';
window.location.href = 'http://google.com#asdf';

const event2 = new Event('popstate');
listeners[0].listener(event2);

expect(spy).toHaveBeenCalledTimes(2);
expect(spy.mock.calls[1][0]).toBe(window.location);
});

test('does not emit if location did not change', async () => {
const spy = jest.fn();

location$.subscribe(spy);

expect(spy).toHaveBeenCalledTimes(0);

const event1 = new Event('popstate');
listeners[0].listener(event1);

expect(spy).toHaveBeenCalledTimes(1);
expect(spy.mock.calls[0][0]).toBe(window.location);

const event2 = new Event('popstate');
listeners[0].listener(event2);

expect(spy).toHaveBeenCalledTimes(1);
});
16 changes: 16 additions & 0 deletions src/location$.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { merge, from, fromEvent, Observable } from 'rxjs';
import { map, share, distinctUntilChanged } from 'rxjs/operators';
import { window } from './window';

export const location$: Observable<Location> = !!window
? merge(
fromEvent(window, 'popstate'),
fromEvent(window, 'pushstate'),
fromEvent(window, 'replacestate'),
).pipe(
share(),
map(() => window!.location.href),
distinctUntilChanged((href1, href2) => href1 === href2),
map(() => window!.location)
)
: from([]);
1 change: 1 addition & 0 deletions src/window.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const window: Window | undefined = typeof document === 'object' ? (global as unknown as Window) : undefined;

0 comments on commit ed69dd2

Please sign in to comment.