-
Notifications
You must be signed in to change notification settings - Fork 44
/
sync-location-hashes.test.js
117 lines (97 loc) · 3.91 KB
/
sync-location-hashes.test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/* eslint-env jest */
import syncLocationHashes from './sync-location-hashes'
const createWindowMock = () => {
const win = {
location: { _hash: '' },
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
}
Object.defineProperty(win.location, 'hash', {
// Store the hash without '#' in the _hash attribute, and prefix the '#' when getting it.
get: jest.fn(function () { return this._hash ? `#${this._hash}` : '' }),
set: jest.fn(function (value) { this._hash = value.replace(/^#/, '') }),
})
return win
}
describe('windowMock', () => {
test('should prepend the # to the hash value', () => {
const win = createWindowMock()
win.location.hash = 'abc'
expect(win.location.hash).toEqual('#abc')
})
test('should not end up with a double ## before the hash value', () => {
const win = createWindowMock()
win.location.hash = '#abc'
expect(win.location.hash).toEqual('#abc')
})
test('should not add a # when the hash is the empty string', () => {
const win = createWindowMock()
win.location.hash = ''
expect(win.location.hash).toEqual('')
})
})
describe('syncLocationHashes', () => {
const win1 = createWindowMock()
const win2 = createWindowMock()
const win3 = createWindowMock()
const windows = [win1, win2, win3]
beforeEach(() => {
win1.location.hash = '#win1hash'
win2.location.hash = '#win2hash'
win3.location.hash = '#win3hash'
windows.forEach(win => {
win.addEventListener.mockReset()
win.removeEventListener.mockReset()
// Do not reset getter/setter implementations, but clear their call log.
// (note that the _hash value is left intact, to the value we just gave it)
const { get, set } = Object.getOwnPropertyDescriptor(win.location, 'hash')
get.mockClear()
set.mockClear()
})
})
test('should create a listener on the windows', () => {
syncLocationHashes(windows)
windows.forEach(win => {
expect(win.addEventListener).toHaveBeenCalledTimes(1)
})
})
test('should disable the listeners when returned function is called', () => {
const disableListener = syncLocationHashes(windows)
windows.forEach(win => {
expect(win.removeEventListener).not.toHaveBeenCalled()
})
disableListener()
windows.forEach(win => {
expect(win.removeEventListener.mock.calls).toEqual(win.addEventListener.mock.calls)
})
})
test('should directly perform an initial sync if specified', () => {
win2.location.hash = '#somehash'
syncLocationHashes(windows, { initial: win2 })
windows.forEach(win => {
expect(win.location.hash).toEqual('#somehash')
})
})
test('should sync to other windows when one emits a hashchange event', () => {
syncLocationHashes(windows)
const win2HashChangeEventListener = win2.addEventListener.mock.calls[0][1]
win2.location.hash = '#newhash'
win2HashChangeEventListener()
windows.forEach(win => {
expect(win.location.hash).toEqual('#newhash')
})
})
test('should avoid creating an infinite updating loop', () => {
// We simply verify that a hash will not be set if it already has the right value.
syncLocationHashes(windows)
const win2HashChangeEventListener = win2.addEventListener.mock.calls[0][1]
win1.location.hash = '#samehash'
win2.location.hash = '#samehash'
win3.location.hash = '#samehash'
win2HashChangeEventListener()
windows.forEach(win => {
const hashSetter = Object.getOwnPropertyDescriptor(win.location, 'hash').set
expect(hashSetter).toHaveBeenCalledTimes(1) // just our manual assignment above.
})
})
})