@openobserve/browser-core 0.2.12-beta.27 → 0.2.12-beta.28
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/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/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/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,41 @@
|
|
|
1
|
+
import { display } from './display'
|
|
2
|
+
import { matchList } from './matchOption'
|
|
3
|
+
|
|
4
|
+
describe('matchList', () => {
|
|
5
|
+
it('should match exact value', () => {
|
|
6
|
+
const list = ['foo', 'bar']
|
|
7
|
+
expect(matchList(list, 'foo')).toBe(true)
|
|
8
|
+
expect(matchList(list, 'bar')).toBe(true)
|
|
9
|
+
expect(matchList(list, 'qux')).toBe(false)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('should match regexp', () => {
|
|
13
|
+
const list = [/^foo/, /foo$/]
|
|
14
|
+
expect(matchList(list, 'foobar')).toBe(true)
|
|
15
|
+
expect(matchList(list, 'barfoo')).toBe(true)
|
|
16
|
+
expect(matchList(list, 'barqux')).toBe(false)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('should match function', () => {
|
|
20
|
+
const list = [(value: string) => value === 'foo', (value: string) => value === 'bar']
|
|
21
|
+
expect(matchList(list, 'foo')).toBe(true)
|
|
22
|
+
expect(matchList(list, 'bar')).toBe(true)
|
|
23
|
+
expect(matchList(list, 'qux')).toBe(false)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('should compare strings using startsWith when enabling the option', () => {
|
|
27
|
+
const list = ['http://my.domain.com']
|
|
28
|
+
expect(matchList(list, 'http://my.domain.com/action', true)).toBe(true)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should catch error from provided function', () => {
|
|
32
|
+
spyOn(display, 'error')
|
|
33
|
+
const list = [
|
|
34
|
+
(_: string) => {
|
|
35
|
+
throw new Error('oops')
|
|
36
|
+
},
|
|
37
|
+
]
|
|
38
|
+
expect(matchList(list, 'foo')).toBe(false)
|
|
39
|
+
expect(display.error).toHaveBeenCalled()
|
|
40
|
+
})
|
|
41
|
+
})
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { deepClone, mergeInto, combine } from './mergeInto'
|
|
2
|
+
|
|
3
|
+
describe('mergeInto', () => {
|
|
4
|
+
describe('source is not an object or array', () => {
|
|
5
|
+
it('should ignore undefined sources', () => {
|
|
6
|
+
const destination = {}
|
|
7
|
+
expect(mergeInto(destination, undefined)).toBe(destination)
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('should ignore undefined destination', () => {
|
|
11
|
+
expect(mergeInto(undefined, 1)).toBe(1)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('should ignore destinations with a different type', () => {
|
|
15
|
+
expect(mergeInto({}, 1)).toBe(1)
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('source is an array', () => {
|
|
20
|
+
it('should create a new array if destination is undefined', () => {
|
|
21
|
+
const source = [1]
|
|
22
|
+
const result = mergeInto(undefined, source)
|
|
23
|
+
expect(result).not.toBe(source)
|
|
24
|
+
expect(result).toEqual(source)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('should return the copy of source if the destination is not an array', () => {
|
|
28
|
+
const source = [1]
|
|
29
|
+
expect(mergeInto({}, source)).toEqual(source)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should mutate and return destination if it is an array', () => {
|
|
33
|
+
const destination = ['destination']
|
|
34
|
+
const source = ['source']
|
|
35
|
+
const result = mergeInto(destination, source)
|
|
36
|
+
expect(result).toBe(destination)
|
|
37
|
+
expect(result).toEqual(source)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe('source is an object', () => {
|
|
42
|
+
it('should create a new object if destination is undefined', () => {
|
|
43
|
+
const source = {}
|
|
44
|
+
const result = mergeInto(undefined, source)
|
|
45
|
+
expect(result).not.toBe(source)
|
|
46
|
+
expect(result).toEqual(source)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should return the copy of source if the destination is not an object', () => {
|
|
50
|
+
const source = { a: 1 }
|
|
51
|
+
expect(mergeInto([], source)).toEqual(source)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should mutate and return destination if it is an object', () => {
|
|
55
|
+
const destination = {}
|
|
56
|
+
const source = { a: 'b' }
|
|
57
|
+
const result = mergeInto(destination, source)
|
|
58
|
+
expect(result).toBe(destination as any)
|
|
59
|
+
expect(result).toEqual(source)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
describe('combine', () => {
|
|
65
|
+
it('should deeply add and replace keys', () => {
|
|
66
|
+
const sourceA = { a: { b: 'toBeReplaced', c: 'source a' } }
|
|
67
|
+
const sourceB = { a: { b: 'replaced', d: 'source b' } }
|
|
68
|
+
expect(combine(sourceA, sourceB)).toEqual({ a: { b: 'replaced', c: 'source a', d: 'source b' } })
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should not replace with undefined', () => {
|
|
72
|
+
expect(combine({ a: 1 }, { a: undefined as number | undefined })).toEqual({ a: 1 })
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should replace a sub-value with null', () => {
|
|
76
|
+
expect(combine({ a: {} }, { a: null as any })).toEqual({ a: null })
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should ignore null arguments', () => {
|
|
80
|
+
expect(combine({ a: 1 }, null)).toEqual({ a: 1 })
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should merge arrays', () => {
|
|
84
|
+
const sourceA = [{ a: 'source a' }, 'extraString'] as any
|
|
85
|
+
const sourceB = [{ b: 'source b' }] as any
|
|
86
|
+
expect(combine(sourceA, sourceB)).toEqual([{ a: 'source a', b: 'source b' }, 'extraString'])
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should merge multiple objects', () => {
|
|
90
|
+
expect(combine({ a: 1 }, { b: 2 }, { c: 3 })).toEqual({ a: 1, b: 2, c: 3 })
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should not keep references on objects', () => {
|
|
94
|
+
const source = { a: { b: 1 } }
|
|
95
|
+
const result = combine({}, source)
|
|
96
|
+
expect(result.a).not.toBe(source.a)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('should not keep references on arrays', () => {
|
|
100
|
+
const source = { a: [1] }
|
|
101
|
+
const result = combine({}, source)
|
|
102
|
+
expect(result.a).not.toBe(source.a)
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
describe('deepClone', () => {
|
|
107
|
+
it('should pass-through primitive values', () => {
|
|
108
|
+
expect(deepClone('test')).toBe('test')
|
|
109
|
+
expect(deepClone(true)).toBe(true)
|
|
110
|
+
expect(deepClone(false)).toBe(false)
|
|
111
|
+
expect(deepClone(null)).toBe(null)
|
|
112
|
+
expect(deepClone(undefined)).toBe(undefined)
|
|
113
|
+
expect(deepClone(1)).toBe(1)
|
|
114
|
+
expect(deepClone(NaN)).toBeNaN()
|
|
115
|
+
expect(deepClone(Infinity)).toBe(Infinity)
|
|
116
|
+
expect(deepClone(-Infinity)).toBe(-Infinity)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('should pass-through functions', () => {
|
|
120
|
+
const fn = () => null
|
|
121
|
+
expect(deepClone(fn)).toBe(fn)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('should pass-through classes', () => {
|
|
125
|
+
class Foo {}
|
|
126
|
+
// typeof class is 'function' so it will behave the same as for function case
|
|
127
|
+
expect(deepClone(Foo)).toBe(Foo)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('should clone array recursively', () => {
|
|
131
|
+
const source = [1, undefined, null, [4, 5, 6]]
|
|
132
|
+
const clone = deepClone(source)
|
|
133
|
+
|
|
134
|
+
expect(clone).toEqual(source)
|
|
135
|
+
expect(clone).not.toBe(source)
|
|
136
|
+
source.push(7)
|
|
137
|
+
;(source[3] as any[]).push(8)
|
|
138
|
+
|
|
139
|
+
expect(clone[4]).toBeUndefined()
|
|
140
|
+
expect((clone[3] as any[])[3]).toBeUndefined()
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('should clone object recursively', () => {
|
|
144
|
+
const source = { foo: 'bar', baz: { arr: [1, 2], fn: () => undefined } }
|
|
145
|
+
const clone = deepClone(source)
|
|
146
|
+
|
|
147
|
+
expect(clone).toEqual(source)
|
|
148
|
+
expect(clone).not.toBe(source)
|
|
149
|
+
source.baz.arr.push(1)
|
|
150
|
+
;(source.baz as any).added = 'test'
|
|
151
|
+
|
|
152
|
+
expect(clone.baz.arr).toEqual([1, 2])
|
|
153
|
+
expect((clone.baz as any).added).toBeUndefined()
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('should clone regexp', () => {
|
|
157
|
+
const source = { reg: /test/gi }
|
|
158
|
+
const clone = deepClone(source)
|
|
159
|
+
|
|
160
|
+
expect(clone).toEqual(source)
|
|
161
|
+
expect(clone).not.toBe(source)
|
|
162
|
+
expect(clone.reg).not.toBe(source.reg)
|
|
163
|
+
|
|
164
|
+
expect(clone.reg.ignoreCase).toBe(true)
|
|
165
|
+
expect(clone.reg.global).toBe(true)
|
|
166
|
+
expect(clone.reg.multiline).toBe(false)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should clone date', () => {
|
|
170
|
+
const source = [1, new Date('2012-12-12')] as const
|
|
171
|
+
const clone = deepClone(source)
|
|
172
|
+
|
|
173
|
+
expect(clone).toEqual(source)
|
|
174
|
+
expect(clone).not.toBe(source)
|
|
175
|
+
expect(clone[1]).not.toBe(source[1])
|
|
176
|
+
|
|
177
|
+
const originalTime = source[1].getTime()
|
|
178
|
+
source[1].setTime(originalTime + 100)
|
|
179
|
+
expect(clone[1].getTime()).toEqual(originalTime)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('should remove circular references', () => {
|
|
183
|
+
const a: Record<string, any> = { foo: 'bar', ref: null }
|
|
184
|
+
const b: Record<string, any> = { baz: 'bar', ref: null }
|
|
185
|
+
// create circular reference
|
|
186
|
+
a.ref = b
|
|
187
|
+
b.ref = a
|
|
188
|
+
|
|
189
|
+
const clonedA = deepClone(a)
|
|
190
|
+
const clonedB = deepClone(b)
|
|
191
|
+
|
|
192
|
+
expect(clonedA).not.toEqual(a)
|
|
193
|
+
expect(clonedA.ref.ref).toBeUndefined()
|
|
194
|
+
|
|
195
|
+
expect(clonedB).not.toEqual(b)
|
|
196
|
+
expect(clonedB.ref.ref).toBeUndefined()
|
|
197
|
+
})
|
|
198
|
+
})
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { display } from './display'
|
|
2
|
+
import { callMonitored, monitor, monitored, startMonitorErrorCollection, resetMonitor, setDebugMode } from './monitor'
|
|
3
|
+
|
|
4
|
+
describe('monitor', () => {
|
|
5
|
+
let onMonitorErrorCollectedSpy: jasmine.Spy<(error: unknown) => void>
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
onMonitorErrorCollectedSpy = jasmine.createSpy()
|
|
9
|
+
})
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
resetMonitor()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
describe('decorator', () => {
|
|
15
|
+
class Candidate {
|
|
16
|
+
@monitored
|
|
17
|
+
monitoredThrowing() {
|
|
18
|
+
throw new Error('monitored')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@monitored
|
|
22
|
+
monitoredStringErrorThrowing() {
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
24
|
+
throw 'string error'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@monitored
|
|
28
|
+
monitoredObjectErrorThrowing() {
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
30
|
+
throw { foo: 'bar' }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@monitored
|
|
34
|
+
monitoredNotThrowing() {
|
|
35
|
+
return 1
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
notMonitoredThrowing() {
|
|
39
|
+
throw new Error('not monitored')
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let candidate: Candidate
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
candidate = new Candidate()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('before initialization', () => {
|
|
49
|
+
it('should not monitor', () => {
|
|
50
|
+
expect(() => candidate.notMonitoredThrowing()).toThrowError('not monitored')
|
|
51
|
+
expect(() => candidate.monitoredThrowing()).toThrowError('monitored')
|
|
52
|
+
expect(candidate.monitoredNotThrowing()).toEqual(1)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
describe('after initialization', () => {
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
startMonitorErrorCollection(onMonitorErrorCollectedSpy)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('should preserve original behavior', () => {
|
|
62
|
+
expect(candidate.monitoredNotThrowing()).toEqual(1)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('should catch error', () => {
|
|
66
|
+
expect(() => candidate.notMonitoredThrowing()).toThrowError()
|
|
67
|
+
expect(() => candidate.monitoredThrowing()).not.toThrowError()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should report error', () => {
|
|
71
|
+
candidate.monitoredThrowing()
|
|
72
|
+
|
|
73
|
+
expect(onMonitorErrorCollectedSpy).toHaveBeenCalledOnceWith(new Error('monitored'))
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('should report string error', () => {
|
|
77
|
+
candidate.monitoredStringErrorThrowing()
|
|
78
|
+
|
|
79
|
+
expect(onMonitorErrorCollectedSpy).toHaveBeenCalledOnceWith('string error')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('should report object error', () => {
|
|
83
|
+
candidate.monitoredObjectErrorThrowing()
|
|
84
|
+
|
|
85
|
+
expect(onMonitorErrorCollectedSpy).toHaveBeenCalledOnceWith({ foo: 'bar' })
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
describe('function', () => {
|
|
91
|
+
const notThrowing = () => 1
|
|
92
|
+
const throwing = () => {
|
|
93
|
+
throw new Error('error')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
beforeEach(() => {
|
|
97
|
+
startMonitorErrorCollection(onMonitorErrorCollectedSpy)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe('direct call', () => {
|
|
101
|
+
it('should preserve original behavior', () => {
|
|
102
|
+
expect(callMonitored(notThrowing)).toEqual(1)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should catch error', () => {
|
|
106
|
+
expect(() => callMonitored(throwing)).not.toThrowError()
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('should report error', () => {
|
|
110
|
+
callMonitored(throwing)
|
|
111
|
+
|
|
112
|
+
expect(onMonitorErrorCollectedSpy).toHaveBeenCalledOnceWith(new Error('error'))
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
describe('wrapper', () => {
|
|
117
|
+
it('should preserve original behavior', () => {
|
|
118
|
+
const decorated = monitor(notThrowing)
|
|
119
|
+
expect(decorated()).toEqual(1)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('should catch error', () => {
|
|
123
|
+
const decorated = monitor(throwing)
|
|
124
|
+
expect(() => decorated()).not.toThrowError()
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('should report error', () => {
|
|
128
|
+
monitor(throwing)()
|
|
129
|
+
|
|
130
|
+
expect(onMonitorErrorCollectedSpy).toHaveBeenCalledOnceWith(new Error('error'))
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
describe('setDebugMode', () => {
|
|
136
|
+
let displaySpy: jasmine.Spy
|
|
137
|
+
|
|
138
|
+
beforeEach(() => {
|
|
139
|
+
displaySpy = spyOn(display, 'error')
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('when not called, should not display error', () => {
|
|
143
|
+
callMonitored(() => {
|
|
144
|
+
throw new Error('message')
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
expect(displaySpy).not.toHaveBeenCalled()
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('when called, should display error', () => {
|
|
151
|
+
setDebugMode(true)
|
|
152
|
+
|
|
153
|
+
callMonitored(() => {
|
|
154
|
+
throw new Error('message')
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
expect(displaySpy).toHaveBeenCalledOnceWith('[MONITOR]', new Error('message'))
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('displays errors thrown by the onMonitorErrorCollected callback', () => {
|
|
161
|
+
setDebugMode(true)
|
|
162
|
+
onMonitorErrorCollectedSpy.and.throwError(new Error('unexpected'))
|
|
163
|
+
startMonitorErrorCollection(onMonitorErrorCollectedSpy)
|
|
164
|
+
|
|
165
|
+
callMonitored(() => {
|
|
166
|
+
throw new Error('message')
|
|
167
|
+
})
|
|
168
|
+
expect(displaySpy).toHaveBeenCalledWith('[MONITOR]', new Error('message'))
|
|
169
|
+
expect(displaySpy).toHaveBeenCalledWith('[MONITOR]', new Error('unexpected'))
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
})
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { waitNextMicrotask } from '../../test'
|
|
2
|
+
import { BufferedObservable, mergeObservables, Observable } from './observable'
|
|
3
|
+
|
|
4
|
+
describe('observable', () => {
|
|
5
|
+
let observable: Observable<void>
|
|
6
|
+
let subscriber: jasmine.Spy<jasmine.Func>
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
observable = new Observable()
|
|
10
|
+
subscriber = jasmine.createSpy('sub')
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('should allow to subscribe and be notified', () => {
|
|
14
|
+
observable.subscribe(subscriber)
|
|
15
|
+
expect(subscriber).not.toHaveBeenCalled()
|
|
16
|
+
|
|
17
|
+
observable.notify()
|
|
18
|
+
expect(subscriber).toHaveBeenCalledTimes(1)
|
|
19
|
+
|
|
20
|
+
observable.notify()
|
|
21
|
+
expect(subscriber).toHaveBeenCalledTimes(2)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should notify multiple clients', () => {
|
|
25
|
+
const otherSubscriber = jasmine.createSpy('sub2')
|
|
26
|
+
observable.subscribe(subscriber)
|
|
27
|
+
observable.subscribe(otherSubscriber)
|
|
28
|
+
|
|
29
|
+
observable.notify()
|
|
30
|
+
|
|
31
|
+
expect(subscriber).toHaveBeenCalled()
|
|
32
|
+
expect(otherSubscriber).toHaveBeenCalled()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should allow to unsubscribe', () => {
|
|
36
|
+
const subscription = observable.subscribe(subscriber)
|
|
37
|
+
|
|
38
|
+
subscription.unsubscribe()
|
|
39
|
+
observable.notify()
|
|
40
|
+
|
|
41
|
+
expect(subscriber).not.toHaveBeenCalled()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('should execute onFirstSubscribe callback', () => {
|
|
45
|
+
const onFirstSubscribe = jasmine.createSpy('callback')
|
|
46
|
+
const otherSubscriber = jasmine.createSpy('sub2')
|
|
47
|
+
observable = new Observable(onFirstSubscribe)
|
|
48
|
+
expect(onFirstSubscribe).not.toHaveBeenCalled()
|
|
49
|
+
|
|
50
|
+
observable.subscribe(subscriber)
|
|
51
|
+
expect(onFirstSubscribe).toHaveBeenCalledTimes(1)
|
|
52
|
+
|
|
53
|
+
observable.subscribe(otherSubscriber)
|
|
54
|
+
expect(onFirstSubscribe).toHaveBeenCalledTimes(1)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should notify the first subscriber if the onFirstSubscribe callback notifies synchronously ', () => {
|
|
58
|
+
const onFirstSubscribe = jasmine.createSpy('callback').and.callFake((observable: Observable<void>) => {
|
|
59
|
+
observable.notify()
|
|
60
|
+
})
|
|
61
|
+
observable = new Observable(onFirstSubscribe)
|
|
62
|
+
observable.subscribe(subscriber)
|
|
63
|
+
|
|
64
|
+
expect(onFirstSubscribe).toHaveBeenCalledTimes(1)
|
|
65
|
+
expect(subscriber).toHaveBeenCalledTimes(1)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('should pass the observable instance to the onFirstSubscribe callback', () => {
|
|
69
|
+
const onFirstSubscribe = jasmine.createSpy('callback')
|
|
70
|
+
observable = new Observable(onFirstSubscribe)
|
|
71
|
+
observable.subscribe(subscriber)
|
|
72
|
+
|
|
73
|
+
expect(onFirstSubscribe).toHaveBeenCalledWith(observable)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('should execute onLastUnsubscribe callback', () => {
|
|
77
|
+
const onLastUnsubscribe = jasmine.createSpy('callback')
|
|
78
|
+
const otherSubscriber = jasmine.createSpy('sub2')
|
|
79
|
+
observable = new Observable(() => onLastUnsubscribe)
|
|
80
|
+
const subscription = observable.subscribe(subscriber)
|
|
81
|
+
const otherSubscription = observable.subscribe(otherSubscriber)
|
|
82
|
+
expect(onLastUnsubscribe).not.toHaveBeenCalled()
|
|
83
|
+
|
|
84
|
+
subscription.unsubscribe()
|
|
85
|
+
expect(onLastUnsubscribe).not.toHaveBeenCalled()
|
|
86
|
+
|
|
87
|
+
otherSubscription.unsubscribe()
|
|
88
|
+
expect(onLastUnsubscribe).toHaveBeenCalled()
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe('mergeObservables', () => {
|
|
93
|
+
let observableOne: Observable<void>
|
|
94
|
+
let observableTwo: Observable<void>
|
|
95
|
+
let mergedObservable: Observable<void>
|
|
96
|
+
let subscriber: jasmine.Spy<jasmine.Func>
|
|
97
|
+
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
observableOne = new Observable<void>()
|
|
100
|
+
observableTwo = new Observable<void>()
|
|
101
|
+
mergedObservable = mergeObservables(observableOne, observableTwo)
|
|
102
|
+
subscriber = jasmine.createSpy('subscriber')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should notify when one of the merged observable notifies', () => {
|
|
106
|
+
mergedObservable.subscribe(subscriber)
|
|
107
|
+
observableOne.notify()
|
|
108
|
+
observableTwo.notify()
|
|
109
|
+
|
|
110
|
+
expect(subscriber).toHaveBeenCalledTimes(2)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('should allow to unsubscribe to all merged observables', () => {
|
|
114
|
+
const subscription = mergedObservable.subscribe(subscriber)
|
|
115
|
+
|
|
116
|
+
subscription.unsubscribe()
|
|
117
|
+
observableOne.notify()
|
|
118
|
+
observableTwo.notify()
|
|
119
|
+
|
|
120
|
+
expect(subscriber).not.toHaveBeenCalled()
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
describe('BufferedObservable', () => {
|
|
125
|
+
it('invokes the observer with buffered data', async () => {
|
|
126
|
+
const observable = new BufferedObservable<string>(100)
|
|
127
|
+
observable.notify('first')
|
|
128
|
+
observable.notify('second')
|
|
129
|
+
|
|
130
|
+
const observer = jasmine.createSpy('observer')
|
|
131
|
+
observable.subscribe(observer)
|
|
132
|
+
|
|
133
|
+
await waitNextMicrotask()
|
|
134
|
+
|
|
135
|
+
expect(observer).toHaveBeenCalledTimes(2)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('invokes the observer asynchronously', async () => {
|
|
139
|
+
const observable = new BufferedObservable<string>(100)
|
|
140
|
+
observable.notify('first')
|
|
141
|
+
|
|
142
|
+
const observer = jasmine.createSpy('observer')
|
|
143
|
+
observable.subscribe(observer)
|
|
144
|
+
|
|
145
|
+
expect(observer).not.toHaveBeenCalled()
|
|
146
|
+
|
|
147
|
+
await waitNextMicrotask()
|
|
148
|
+
|
|
149
|
+
expect(observer).toHaveBeenCalledWith('first')
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('invokes the observer when new data is notified after subscription', async () => {
|
|
153
|
+
const observable = new BufferedObservable<string>(100)
|
|
154
|
+
|
|
155
|
+
const observer = jasmine.createSpy('observer')
|
|
156
|
+
observable.subscribe(observer)
|
|
157
|
+
|
|
158
|
+
observable.notify('first')
|
|
159
|
+
|
|
160
|
+
await waitNextMicrotask()
|
|
161
|
+
|
|
162
|
+
observable.notify('second')
|
|
163
|
+
|
|
164
|
+
expect(observer).toHaveBeenCalledTimes(2)
|
|
165
|
+
expect(observer).toHaveBeenCalledWith('first')
|
|
166
|
+
expect(observer).toHaveBeenCalledWith('second')
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('drops data when the buffer is full', async () => {
|
|
170
|
+
const observable = new BufferedObservable<string>(2)
|
|
171
|
+
observable.notify('first') // This should be dropped
|
|
172
|
+
observable.notify('second')
|
|
173
|
+
observable.notify('third')
|
|
174
|
+
|
|
175
|
+
const observer = jasmine.createSpy('observer')
|
|
176
|
+
observable.subscribe(observer)
|
|
177
|
+
|
|
178
|
+
await waitNextMicrotask()
|
|
179
|
+
|
|
180
|
+
expect(observer).toHaveBeenCalledTimes(2)
|
|
181
|
+
expect(observer).toHaveBeenCalledWith('second')
|
|
182
|
+
expect(observer).toHaveBeenCalledWith('third')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('allows to unsubscribe from the observer, the middle of buffered data', async () => {
|
|
186
|
+
const observable = new BufferedObservable<string>(100)
|
|
187
|
+
observable.notify('first')
|
|
188
|
+
observable.notify('second')
|
|
189
|
+
|
|
190
|
+
const observer = jasmine.createSpy('observer').and.callFake(() => {
|
|
191
|
+
subscription.unsubscribe()
|
|
192
|
+
})
|
|
193
|
+
const subscription = observable.subscribe(observer)
|
|
194
|
+
|
|
195
|
+
await waitNextMicrotask()
|
|
196
|
+
|
|
197
|
+
expect(observer).toHaveBeenCalledTimes(1)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('allows to unsubscribe before the buffered data', async () => {
|
|
201
|
+
const observable = new BufferedObservable<string>(100)
|
|
202
|
+
observable.notify('first')
|
|
203
|
+
|
|
204
|
+
const observer = jasmine.createSpy('observer')
|
|
205
|
+
const subscription = observable.subscribe(observer)
|
|
206
|
+
|
|
207
|
+
subscription.unsubscribe()
|
|
208
|
+
|
|
209
|
+
await waitNextMicrotask()
|
|
210
|
+
|
|
211
|
+
expect(observer).not.toHaveBeenCalled()
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('allows to unsubscribe after the buffered data', async () => {
|
|
215
|
+
const observable = new BufferedObservable<string>(100)
|
|
216
|
+
|
|
217
|
+
const observer = jasmine.createSpy('observer')
|
|
218
|
+
const subscription = observable.subscribe(observer)
|
|
219
|
+
|
|
220
|
+
await waitNextMicrotask()
|
|
221
|
+
|
|
222
|
+
subscription.unsubscribe()
|
|
223
|
+
|
|
224
|
+
observable.notify('first')
|
|
225
|
+
|
|
226
|
+
expect(observer).not.toHaveBeenCalled()
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('calling unbuffer() removes buffered data', async () => {
|
|
230
|
+
const observable = new BufferedObservable<string>(2)
|
|
231
|
+
observable.notify('first')
|
|
232
|
+
observable.notify('second')
|
|
233
|
+
|
|
234
|
+
observable.unbuffer()
|
|
235
|
+
await waitNextMicrotask()
|
|
236
|
+
|
|
237
|
+
const observer = jasmine.createSpy('observer')
|
|
238
|
+
observable.subscribe(observer)
|
|
239
|
+
await waitNextMicrotask()
|
|
240
|
+
|
|
241
|
+
expect(observer).not.toHaveBeenCalled()
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('when calling unbuffer() right after subscription, buffered data should still be notified', async () => {
|
|
245
|
+
const observable = new BufferedObservable<string>(2)
|
|
246
|
+
observable.notify('first')
|
|
247
|
+
observable.notify('second')
|
|
248
|
+
|
|
249
|
+
const observer = jasmine.createSpy('observer')
|
|
250
|
+
observable.subscribe(observer)
|
|
251
|
+
|
|
252
|
+
observable.unbuffer()
|
|
253
|
+
await waitNextMicrotask()
|
|
254
|
+
|
|
255
|
+
expect(observer).toHaveBeenCalledTimes(2)
|
|
256
|
+
expect(observer).toHaveBeenCalledWith('first')
|
|
257
|
+
expect(observer).toHaveBeenCalledWith('second')
|
|
258
|
+
})
|
|
259
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { startMockTelemetry, waitNextMicrotask } from '../../test'
|
|
2
|
+
import { queueMicrotask } from './queueMicrotask'
|
|
3
|
+
|
|
4
|
+
describe('queueMicrotask', () => {
|
|
5
|
+
it('calls the callback in a microtask', async () => {
|
|
6
|
+
let called = false
|
|
7
|
+
queueMicrotask(() => {
|
|
8
|
+
called = true
|
|
9
|
+
})
|
|
10
|
+
expect(called).toBe(false)
|
|
11
|
+
await waitNextMicrotask()
|
|
12
|
+
expect(called).toBe(true)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('monitors the callback', async () => {
|
|
16
|
+
const telemetry = startMockTelemetry()
|
|
17
|
+
queueMicrotask(() => {
|
|
18
|
+
throw new Error('test error')
|
|
19
|
+
})
|
|
20
|
+
await waitNextMicrotask()
|
|
21
|
+
|
|
22
|
+
expect(await telemetry.hasEvents()).toBe(true)
|
|
23
|
+
})
|
|
24
|
+
})
|