@openobserve/browser-core 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/boot/init.js +1 -1
- package/cjs/domain/configuration/endpointBuilder.js +1 -1
- package/cjs/domain/tags.js +1 -1
- package/cjs/domain/telemetry/telemetry.js +1 -1
- package/cjs/domain/telemetry/telemetryEvent.types.d.ts +1 -1
- package/esm/boot/init.js +1 -1
- package/esm/domain/configuration/endpointBuilder.js +1 -1
- package/esm/domain/tags.js +1 -1
- package/esm/domain/telemetry/telemetry.js +1 -1
- package/esm/domain/telemetry/telemetryEvent.types.d.ts +1 -1
- package/package.json +2 -2
- package/src/boot/displayAlreadyInitializedError.spec.ts +18 -0
- package/src/boot/init.spec.ts +50 -0
- package/src/browser/addEventListener.spec.ts +141 -0
- package/src/browser/cookie.spec.ts +53 -0
- package/src/browser/fetchObservable.spec.ts +334 -0
- package/src/browser/pageMayExitObservable.spec.ts +56 -0
- package/src/browser/xhrObservable.spec.ts +405 -0
- package/src/domain/allowedTrackingOrigins.spec.ts +210 -0
- package/src/domain/bufferedData.spec.ts +34 -0
- package/src/domain/configuration/configuration.spec.ts +236 -0
- package/src/domain/configuration/endpointBuilder.spec.ts +173 -0
- package/src/domain/configuration/transportConfiguration.spec.ts +130 -0
- package/src/domain/connectivity/connectivity.spec.ts +50 -0
- package/src/domain/console/consoleObservable.spec.ts +151 -0
- package/src/domain/context/contextManager.spec.ts +152 -0
- package/src/domain/context/contextUtils.spec.ts +26 -0
- package/src/domain/context/storeContextManager.spec.ts +103 -0
- package/src/domain/contexts/accountContext.spec.ts +99 -0
- package/src/domain/contexts/globalContext.spec.ts +88 -0
- package/src/domain/contexts/userContext.spec.ts +156 -0
- package/src/domain/error/error.spec.ts +294 -0
- package/src/domain/error/trackRuntimeError.spec.ts +343 -0
- package/src/domain/eventRateLimiter/createEventRateLimiter.spec.ts +119 -0
- package/src/domain/extension/extensionUtils.spec.ts +47 -0
- package/src/domain/report/reportObservable.spec.ts +71 -0
- package/src/domain/session/oldCookiesMigration.spec.ts +76 -0
- package/src/domain/session/sessionManager.spec.ts +684 -0
- package/src/domain/session/sessionState.spec.ts +88 -0
- package/src/domain/session/sessionStore.spec.ts +647 -0
- package/src/domain/session/sessionStoreOperations.spec.ts +234 -0
- package/src/domain/session/storeStrategies/sessionInCookie.spec.ts +228 -0
- package/src/domain/session/storeStrategies/sessionInLocalStorage.spec.ts +75 -0
- package/src/domain/synthetics/syntheticsWorkerValues.spec.ts +82 -0
- package/src/domain/tags.spec.ts +74 -0
- package/src/domain/telemetry/telemetry.spec.ts +494 -0
- package/src/domain/telemetry/telemetryEvent.types.ts +1 -1
- package/src/domain/trackingConsent.spec.ts +44 -0
- package/src/tools/abstractHooks.spec.ts +76 -0
- package/src/tools/abstractLifeCycle.spec.ts +46 -0
- package/src/tools/boundedBuffer.spec.ts +40 -0
- package/src/tools/catchUserErrors.spec.ts +20 -0
- package/src/tools/encoder.spec.ts +112 -0
- package/src/tools/experimentalFeatures.spec.ts +60 -0
- package/src/tools/getZoneJsOriginalValue.spec.ts +36 -0
- package/src/tools/instrumentMethod.spec.ts +381 -0
- package/src/tools/matchOption.spec.ts +41 -0
- package/src/tools/mergeInto.spec.ts +198 -0
- package/src/tools/monitor.spec.ts +172 -0
- package/src/tools/observable.spec.ts +259 -0
- package/src/tools/queueMicrotask.spec.ts +24 -0
- package/src/tools/readBytesFromStream.spec.ts +62 -0
- package/src/tools/requestIdleCallback.spec.ts +65 -0
- package/src/tools/serialisation/jsonStringify.spec.ts +76 -0
- package/src/tools/serialisation/sanitize.spec.ts +284 -0
- package/src/tools/stackTrace/capturedExceptions.specHelper.ts +316 -0
- package/src/tools/stackTrace/computeStackTrace.spec.ts +1007 -0
- package/src/tools/stackTrace/handlingStack.spec.ts +20 -0
- package/src/tools/taskQueue.spec.ts +60 -0
- package/src/tools/timer.spec.ts +76 -0
- package/src/tools/utils/browserDetection.spec.ts +120 -0
- package/src/tools/utils/byteUtils.spec.ts +29 -0
- package/src/tools/utils/functionUtils.spec.ts +229 -0
- package/src/tools/utils/numberUtils.spec.ts +27 -0
- package/src/tools/utils/stringUtils.spec.ts +74 -0
- package/src/tools/utils/typeUtils.spec.ts +25 -0
- package/src/tools/utils/urlPolyfill.spec.ts +55 -0
- package/src/tools/valueHistory.spec.ts +180 -0
- package/src/transport/batch.spec.ts +261 -0
- package/src/transport/eventBridge.spec.ts +90 -0
- package/src/transport/flushController.spec.ts +267 -0
- package/src/transport/httpRequest.spec.ts +400 -0
- package/src/transport/sendWithRetryStrategy.spec.ts +393 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import type { MockFetch, MockFetchManager } from '../../test'
|
|
2
|
+
import { registerCleanupTask, mockFetch } from '../../test'
|
|
3
|
+
import type { Subscription } from '../tools/observable'
|
|
4
|
+
import type { FetchResolveContext, FetchContext } from './fetchObservable'
|
|
5
|
+
import { initFetchObservable, resetFetchObservable, ResponseBodyAction } from './fetchObservable'
|
|
6
|
+
|
|
7
|
+
describe('fetch proxy', () => {
|
|
8
|
+
const FAKE_URL = 'http://fake-url/'
|
|
9
|
+
const FAKE_RELATIVE_URL = '/fake-path'
|
|
10
|
+
const NORMALIZED_FAKE_RELATIVE_URL = `${location.origin}/fake-path`
|
|
11
|
+
let mockFetchManager: MockFetchManager
|
|
12
|
+
let requestsTrackingSubscription: Subscription
|
|
13
|
+
let contextEditionSubscription: Subscription | undefined
|
|
14
|
+
let requests: FetchResolveContext[]
|
|
15
|
+
let originalMockFetch: typeof window.fetch
|
|
16
|
+
let fetch: MockFetch
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
mockFetchManager = mockFetch()
|
|
20
|
+
originalMockFetch = window.fetch
|
|
21
|
+
|
|
22
|
+
requests = []
|
|
23
|
+
requestsTrackingSubscription = initFetchObservable().subscribe((context) => {
|
|
24
|
+
if (context.state === 'resolve') {
|
|
25
|
+
requests.push(context)
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
fetch = window.fetch as MockFetch
|
|
29
|
+
|
|
30
|
+
registerCleanupTask(() => {
|
|
31
|
+
requestsTrackingSubscription.unsubscribe()
|
|
32
|
+
contextEditionSubscription?.unsubscribe()
|
|
33
|
+
resetFetchObservable()
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should track server error', (done) => {
|
|
38
|
+
fetch(FAKE_URL).resolveWith({ status: 500, responseText: 'fetch error' })
|
|
39
|
+
|
|
40
|
+
mockFetchManager.whenAllComplete(() => {
|
|
41
|
+
const request = requests[0]
|
|
42
|
+
expect(request.method).toEqual('GET')
|
|
43
|
+
expect(request.url).toEqual(FAKE_URL)
|
|
44
|
+
expect(request.status).toEqual(500)
|
|
45
|
+
expect(request.isAborted).toBe(false)
|
|
46
|
+
expect(request.handlingStack).toBeDefined()
|
|
47
|
+
done()
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should track refused fetch', (done) => {
|
|
52
|
+
fetch(FAKE_URL).rejectWith(new Error('fetch error'))
|
|
53
|
+
|
|
54
|
+
mockFetchManager.whenAllComplete(() => {
|
|
55
|
+
const request = requests[0]
|
|
56
|
+
expect(request.method).toEqual('GET')
|
|
57
|
+
expect(request.url).toEqual(FAKE_URL)
|
|
58
|
+
expect(request.status).toEqual(0)
|
|
59
|
+
expect(request.isAborted).toBe(false)
|
|
60
|
+
expect(request.error).toEqual(new Error('fetch error'))
|
|
61
|
+
expect(request.handlingStack).toBeDefined()
|
|
62
|
+
done()
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should track aborted fetch', (done) => {
|
|
67
|
+
fetch(FAKE_URL).abort()
|
|
68
|
+
|
|
69
|
+
mockFetchManager.whenAllComplete(() => {
|
|
70
|
+
const request = requests[0]
|
|
71
|
+
expect(request.method).toEqual('GET')
|
|
72
|
+
expect(request.url).toEqual(FAKE_URL)
|
|
73
|
+
expect(request.status).toEqual(0)
|
|
74
|
+
expect(request.isAborted).toBe(true)
|
|
75
|
+
expect(request.error).toEqual(new DOMException('The user aborted a request', 'AbortError'))
|
|
76
|
+
expect(request.handlingStack).toBeDefined()
|
|
77
|
+
done()
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('should track fetch aborted by AbortController', (done) => {
|
|
82
|
+
if (!window.AbortController) {
|
|
83
|
+
pending('AbortController is not supported')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const controller = new AbortController()
|
|
87
|
+
void fetch(FAKE_URL, { signal: controller.signal })
|
|
88
|
+
controller.abort('AbortError')
|
|
89
|
+
|
|
90
|
+
mockFetchManager.whenAllComplete(() => {
|
|
91
|
+
const request = requests[0]
|
|
92
|
+
expect(request.method).toEqual('GET')
|
|
93
|
+
expect(request.url).toEqual(FAKE_URL)
|
|
94
|
+
expect(request.status).toEqual(0)
|
|
95
|
+
expect(request.isAborted).toBe(true)
|
|
96
|
+
expect(request.error).toEqual(controller.signal.reason)
|
|
97
|
+
expect(request.handlingStack).toBeDefined()
|
|
98
|
+
done()
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should track opaque fetch', (done) => {
|
|
103
|
+
// https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
|
|
104
|
+
fetch(FAKE_URL).resolveWith({ status: 0, type: 'opaque' })
|
|
105
|
+
|
|
106
|
+
mockFetchManager.whenAllComplete(() => {
|
|
107
|
+
const request = requests[0]
|
|
108
|
+
expect(request.method).toEqual('GET')
|
|
109
|
+
expect(request.url).toEqual(FAKE_URL)
|
|
110
|
+
expect(request.status).toEqual(0)
|
|
111
|
+
expect(request.isAborted).toBe(false)
|
|
112
|
+
done()
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('should track client error', (done) => {
|
|
117
|
+
fetch(FAKE_URL).resolveWith({ status: 400, responseText: 'Not found' })
|
|
118
|
+
|
|
119
|
+
mockFetchManager.whenAllComplete(() => {
|
|
120
|
+
const request = requests[0]
|
|
121
|
+
expect(request.method).toEqual('GET')
|
|
122
|
+
expect(request.url).toEqual(FAKE_URL)
|
|
123
|
+
expect(request.status).toEqual(400)
|
|
124
|
+
expect(request.isAborted).toBe(false)
|
|
125
|
+
done()
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('should get method from input', (done) => {
|
|
130
|
+
fetch(FAKE_URL).resolveWith({ status: 500 })
|
|
131
|
+
fetch(new Request(FAKE_URL)).resolveWith({ status: 500 })
|
|
132
|
+
fetch(new Request(FAKE_URL, { method: 'PUT' })).resolveWith({ status: 500 })
|
|
133
|
+
fetch(new Request(FAKE_URL, { method: 'PUT' }), { method: 'POST' }).resolveWith({ status: 500 })
|
|
134
|
+
fetch(new Request(FAKE_URL), { method: 'POST' }).resolveWith({ status: 500 })
|
|
135
|
+
fetch(FAKE_URL, { method: 'POST' }).resolveWith({ status: 500 })
|
|
136
|
+
fetch(FAKE_URL, { method: 'post' }).resolveWith({ status: 500 })
|
|
137
|
+
fetch(null as any).resolveWith({ status: 500 })
|
|
138
|
+
fetch({ method: 'POST' } as any).resolveWith({ status: 500 })
|
|
139
|
+
fetch(FAKE_URL, { method: null as any }).resolveWith({ status: 500 })
|
|
140
|
+
fetch(FAKE_URL, { method: undefined }).resolveWith({ status: 500 })
|
|
141
|
+
|
|
142
|
+
mockFetchManager.whenAllComplete(() => {
|
|
143
|
+
expect(requests[0].method).toEqual('GET')
|
|
144
|
+
expect(requests[1].method).toEqual('GET')
|
|
145
|
+
expect(requests[2].method).toEqual('PUT')
|
|
146
|
+
expect(requests[3].method).toEqual('POST')
|
|
147
|
+
expect(requests[4].method).toEqual('POST')
|
|
148
|
+
expect(requests[5].method).toEqual('POST')
|
|
149
|
+
expect(requests[6].method).toEqual('POST')
|
|
150
|
+
expect(requests[7].method).toEqual('GET')
|
|
151
|
+
expect(requests[8].method).toEqual('GET')
|
|
152
|
+
expect(requests[9].method).toEqual('NULL')
|
|
153
|
+
expect(requests[10].method).toEqual('GET')
|
|
154
|
+
|
|
155
|
+
done()
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('should get the normalized url from input', (done) => {
|
|
160
|
+
fetch(FAKE_URL).rejectWith(new Error('fetch error'))
|
|
161
|
+
fetch(new Request(FAKE_URL)).rejectWith(new Error('fetch error'))
|
|
162
|
+
fetch(null as any).rejectWith(new Error('fetch error'))
|
|
163
|
+
fetch({
|
|
164
|
+
toString() {
|
|
165
|
+
return FAKE_RELATIVE_URL
|
|
166
|
+
},
|
|
167
|
+
} as any).rejectWith(new Error('fetch error'))
|
|
168
|
+
fetch(FAKE_RELATIVE_URL).rejectWith(new Error('fetch error'))
|
|
169
|
+
fetch(new Request(FAKE_RELATIVE_URL)).rejectWith(new Error('fetch error'))
|
|
170
|
+
|
|
171
|
+
mockFetchManager.whenAllComplete(() => {
|
|
172
|
+
expect(requests[0].url).toEqual(FAKE_URL)
|
|
173
|
+
expect(requests[1].url).toEqual(FAKE_URL)
|
|
174
|
+
expect(requests[2].url).toMatch(/\/null$/)
|
|
175
|
+
expect(requests[3].url).toEqual(NORMALIZED_FAKE_RELATIVE_URL)
|
|
176
|
+
expect(requests[4].url).toEqual(NORMALIZED_FAKE_RELATIVE_URL)
|
|
177
|
+
expect(requests[5].url).toEqual(NORMALIZED_FAKE_RELATIVE_URL)
|
|
178
|
+
done()
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('should keep promise resolved behavior for Response', (done) => {
|
|
183
|
+
const mockFetchPromise = fetch(FAKE_URL)
|
|
184
|
+
const spy = jasmine.createSpy()
|
|
185
|
+
mockFetchPromise.then(spy).catch(() => {
|
|
186
|
+
fail('Should not have thrown an error!')
|
|
187
|
+
})
|
|
188
|
+
mockFetchPromise.resolveWith({ status: 500 })
|
|
189
|
+
|
|
190
|
+
setTimeout(() => {
|
|
191
|
+
expect(spy).toHaveBeenCalled()
|
|
192
|
+
done()
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('should keep promise resolved behavior for any other type', (done) => {
|
|
197
|
+
const mockFetchPromise = fetch(FAKE_URL)
|
|
198
|
+
const spy = jasmine.createSpy()
|
|
199
|
+
mockFetchPromise.then(spy).catch(() => {
|
|
200
|
+
fail('Should not have thrown an error!')
|
|
201
|
+
})
|
|
202
|
+
mockFetchPromise.resolveWith('response' as any)
|
|
203
|
+
|
|
204
|
+
setTimeout(() => {
|
|
205
|
+
expect(spy).toHaveBeenCalled()
|
|
206
|
+
done()
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it('should keep promise rejected behavior for Error', (done) => {
|
|
211
|
+
const mockFetchPromise = fetch(FAKE_URL)
|
|
212
|
+
const spy = jasmine.createSpy()
|
|
213
|
+
mockFetchPromise.catch(spy)
|
|
214
|
+
mockFetchPromise.rejectWith(new Error('fetch error'))
|
|
215
|
+
|
|
216
|
+
setTimeout(() => {
|
|
217
|
+
expect(spy).toHaveBeenCalled()
|
|
218
|
+
done()
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('should keep promise rejected behavior for any other type', (done) => {
|
|
223
|
+
const mockFetchPromise = fetch(FAKE_URL)
|
|
224
|
+
const spy = jasmine.createSpy()
|
|
225
|
+
mockFetchPromise.catch(spy)
|
|
226
|
+
mockFetchPromise.rejectWith('fetch error' as any)
|
|
227
|
+
|
|
228
|
+
setTimeout(() => {
|
|
229
|
+
expect(spy).toHaveBeenCalled()
|
|
230
|
+
done()
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it('should allow to enhance the context', (done) => {
|
|
235
|
+
type CustomContext = FetchContext & { foo: string }
|
|
236
|
+
contextEditionSubscription = initFetchObservable().subscribe((rawContext) => {
|
|
237
|
+
const context = rawContext as CustomContext
|
|
238
|
+
if (context.state === 'start') {
|
|
239
|
+
context.foo = 'bar'
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
fetch(FAKE_URL).resolveWith({ status: 500, responseText: 'fetch error' })
|
|
243
|
+
|
|
244
|
+
mockFetchManager.whenAllComplete(() => {
|
|
245
|
+
expect((requests[0] as CustomContext).foo).toBe('bar')
|
|
246
|
+
done()
|
|
247
|
+
})
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
describe('when unsubscribing', () => {
|
|
251
|
+
it('should stop tracking requests', (done) => {
|
|
252
|
+
requestsTrackingSubscription.unsubscribe()
|
|
253
|
+
|
|
254
|
+
fetch(FAKE_URL).resolveWith({ status: 200, responseText: 'ok' })
|
|
255
|
+
|
|
256
|
+
mockFetchManager.whenAllComplete(() => {
|
|
257
|
+
expect(requests).toEqual([])
|
|
258
|
+
done()
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('should restore original window.fetch', () => {
|
|
263
|
+
requestsTrackingSubscription.unsubscribe()
|
|
264
|
+
|
|
265
|
+
expect(window.fetch).toBe(originalMockFetch)
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
describe('fetch proxy with ResponseBodyAction', () => {
|
|
271
|
+
const FAKE_URL = 'http://fake-url/'
|
|
272
|
+
let mockFetchManager: MockFetchManager
|
|
273
|
+
let requestsTrackingSubscription: Subscription
|
|
274
|
+
let requests: FetchResolveContext[]
|
|
275
|
+
let fetch: MockFetch
|
|
276
|
+
|
|
277
|
+
function setupFetchTracking(responseBodyAction: () => ResponseBodyAction) {
|
|
278
|
+
mockFetchManager = mockFetch()
|
|
279
|
+
requests = []
|
|
280
|
+
requestsTrackingSubscription = initFetchObservable({ responseBodyAction }).subscribe((context) => {
|
|
281
|
+
if (context.state === 'resolve') {
|
|
282
|
+
requests.push(context)
|
|
283
|
+
}
|
|
284
|
+
})
|
|
285
|
+
fetch = window.fetch as MockFetch
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
afterEach(() => {
|
|
289
|
+
requestsTrackingSubscription?.unsubscribe()
|
|
290
|
+
resetFetchObservable()
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('should collect response body with COLLECT action', (done) => {
|
|
294
|
+
setupFetchTracking(() => ResponseBodyAction.COLLECT)
|
|
295
|
+
|
|
296
|
+
fetch(FAKE_URL).resolveWith({ status: 200, responseText: 'response body content' })
|
|
297
|
+
|
|
298
|
+
mockFetchManager.whenAllComplete(() => {
|
|
299
|
+
expect(requests[0].responseBody).toBe('response body content')
|
|
300
|
+
done()
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
it('should not collect response body with WAIT or IGNORE action', (done) => {
|
|
305
|
+
setupFetchTracking(() => ResponseBodyAction.WAIT)
|
|
306
|
+
|
|
307
|
+
fetch(FAKE_URL).resolveWith({ status: 200, responseText: 'response body content' })
|
|
308
|
+
|
|
309
|
+
mockFetchManager.whenAllComplete(() => {
|
|
310
|
+
expect(requests[0].responseBody).toBeUndefined()
|
|
311
|
+
done()
|
|
312
|
+
})
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
it('should use the highest priority action when multiple getters are registered', (done) => {
|
|
316
|
+
setupFetchTracking(() => ResponseBodyAction.WAIT)
|
|
317
|
+
|
|
318
|
+
initFetchObservable({
|
|
319
|
+
responseBodyAction: () => ResponseBodyAction.COLLECT,
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
registerCleanupTask(() => {
|
|
323
|
+
requestsTrackingSubscription.unsubscribe()
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
fetch = window.fetch as MockFetch
|
|
327
|
+
fetch(FAKE_URL).resolveWith({ status: 200, responseText: 'response body content' })
|
|
328
|
+
|
|
329
|
+
mockFetchManager.whenAllComplete(() => {
|
|
330
|
+
expect(requests[0].responseBody).toBe('response body content')
|
|
331
|
+
done()
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Configuration } from '../domain/configuration'
|
|
2
|
+
import { createNewEvent, restorePageVisibility, setPageVisibility, registerCleanupTask } from '../../test'
|
|
3
|
+
import type { PageMayExitEvent } from './pageMayExitObservable'
|
|
4
|
+
import { PageExitReason, createPageMayExitObservable } from './pageMayExitObservable'
|
|
5
|
+
|
|
6
|
+
describe('createPageMayExitObservable', () => {
|
|
7
|
+
let onExitSpy: jasmine.Spy<(event: PageMayExitEvent) => void>
|
|
8
|
+
let configuration: Configuration
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
onExitSpy = jasmine.createSpy()
|
|
12
|
+
configuration = {} as Configuration
|
|
13
|
+
registerCleanupTask(createPageMayExitObservable(configuration).subscribe(onExitSpy).unsubscribe)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
restorePageVisibility()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('notifies when the page fires beforeunload', () => {
|
|
21
|
+
window.dispatchEvent(createNewEvent('beforeunload'))
|
|
22
|
+
|
|
23
|
+
expect(onExitSpy).toHaveBeenCalledOnceWith({ reason: PageExitReason.UNLOADING })
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('notifies when the page becomes hidden', () => {
|
|
27
|
+
emulatePageVisibilityChange('hidden')
|
|
28
|
+
|
|
29
|
+
expect(onExitSpy).toHaveBeenCalledOnceWith({ reason: PageExitReason.HIDDEN })
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('notifies when the page becomes frozen', () => {
|
|
33
|
+
window.dispatchEvent(createNewEvent('freeze'))
|
|
34
|
+
|
|
35
|
+
expect(onExitSpy).toHaveBeenCalledOnceWith({ reason: PageExitReason.FROZEN })
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('notifies multiple times', () => {
|
|
39
|
+
window.dispatchEvent(createNewEvent('beforeunload'))
|
|
40
|
+
window.dispatchEvent(createNewEvent('beforeunload'))
|
|
41
|
+
emulatePageVisibilityChange('hidden')
|
|
42
|
+
|
|
43
|
+
expect(onExitSpy).toHaveBeenCalledTimes(3)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('does not notify when the page becomes visible', () => {
|
|
47
|
+
emulatePageVisibilityChange('visible')
|
|
48
|
+
|
|
49
|
+
expect(onExitSpy).not.toHaveBeenCalled()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
function emulatePageVisibilityChange(visibility: 'visible' | 'hidden') {
|
|
53
|
+
setPageVisibility(visibility)
|
|
54
|
+
document.dispatchEvent(createNewEvent('visibilitychange'))
|
|
55
|
+
}
|
|
56
|
+
})
|