@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.
Files changed (80) hide show
  1. package/cjs/boot/init.js +1 -1
  2. package/cjs/domain/configuration/endpointBuilder.js +1 -1
  3. package/cjs/domain/tags.js +1 -1
  4. package/cjs/domain/telemetry/telemetry.js +1 -1
  5. package/esm/boot/init.js +1 -1
  6. package/esm/domain/configuration/endpointBuilder.js +1 -1
  7. package/esm/domain/tags.js +1 -1
  8. package/esm/domain/telemetry/telemetry.js +1 -1
  9. package/package.json +2 -2
  10. package/src/boot/displayAlreadyInitializedError.spec.ts +18 -0
  11. package/src/boot/init.spec.ts +50 -0
  12. package/src/browser/addEventListener.spec.ts +141 -0
  13. package/src/browser/cookie.spec.ts +53 -0
  14. package/src/browser/fetchObservable.spec.ts +334 -0
  15. package/src/browser/pageMayExitObservable.spec.ts +56 -0
  16. package/src/browser/xhrObservable.spec.ts +405 -0
  17. package/src/domain/allowedTrackingOrigins.spec.ts +210 -0
  18. package/src/domain/bufferedData.spec.ts +34 -0
  19. package/src/domain/configuration/configuration.spec.ts +236 -0
  20. package/src/domain/configuration/endpointBuilder.spec.ts +173 -0
  21. package/src/domain/configuration/transportConfiguration.spec.ts +130 -0
  22. package/src/domain/connectivity/connectivity.spec.ts +50 -0
  23. package/src/domain/console/consoleObservable.spec.ts +151 -0
  24. package/src/domain/context/contextManager.spec.ts +152 -0
  25. package/src/domain/context/contextUtils.spec.ts +26 -0
  26. package/src/domain/context/storeContextManager.spec.ts +103 -0
  27. package/src/domain/contexts/accountContext.spec.ts +99 -0
  28. package/src/domain/contexts/globalContext.spec.ts +88 -0
  29. package/src/domain/contexts/userContext.spec.ts +156 -0
  30. package/src/domain/error/error.spec.ts +294 -0
  31. package/src/domain/error/trackRuntimeError.spec.ts +343 -0
  32. package/src/domain/eventRateLimiter/createEventRateLimiter.spec.ts +119 -0
  33. package/src/domain/extension/extensionUtils.spec.ts +47 -0
  34. package/src/domain/report/reportObservable.spec.ts +71 -0
  35. package/src/domain/session/oldCookiesMigration.spec.ts +76 -0
  36. package/src/domain/session/sessionManager.spec.ts +684 -0
  37. package/src/domain/session/sessionState.spec.ts +88 -0
  38. package/src/domain/session/sessionStore.spec.ts +647 -0
  39. package/src/domain/session/sessionStoreOperations.spec.ts +234 -0
  40. package/src/domain/session/storeStrategies/sessionInCookie.spec.ts +228 -0
  41. package/src/domain/session/storeStrategies/sessionInLocalStorage.spec.ts +75 -0
  42. package/src/domain/synthetics/syntheticsWorkerValues.spec.ts +82 -0
  43. package/src/domain/tags.spec.ts +74 -0
  44. package/src/domain/telemetry/telemetry.spec.ts +494 -0
  45. package/src/domain/trackingConsent.spec.ts +44 -0
  46. package/src/tools/abstractHooks.spec.ts +76 -0
  47. package/src/tools/abstractLifeCycle.spec.ts +46 -0
  48. package/src/tools/boundedBuffer.spec.ts +40 -0
  49. package/src/tools/catchUserErrors.spec.ts +20 -0
  50. package/src/tools/encoder.spec.ts +112 -0
  51. package/src/tools/experimentalFeatures.spec.ts +60 -0
  52. package/src/tools/getZoneJsOriginalValue.spec.ts +36 -0
  53. package/src/tools/instrumentMethod.spec.ts +381 -0
  54. package/src/tools/matchOption.spec.ts +41 -0
  55. package/src/tools/mergeInto.spec.ts +198 -0
  56. package/src/tools/monitor.spec.ts +172 -0
  57. package/src/tools/observable.spec.ts +259 -0
  58. package/src/tools/queueMicrotask.spec.ts +24 -0
  59. package/src/tools/readBytesFromStream.spec.ts +62 -0
  60. package/src/tools/requestIdleCallback.spec.ts +65 -0
  61. package/src/tools/serialisation/jsonStringify.spec.ts +76 -0
  62. package/src/tools/serialisation/sanitize.spec.ts +284 -0
  63. package/src/tools/stackTrace/capturedExceptions.specHelper.ts +316 -0
  64. package/src/tools/stackTrace/computeStackTrace.spec.ts +1007 -0
  65. package/src/tools/stackTrace/handlingStack.spec.ts +20 -0
  66. package/src/tools/taskQueue.spec.ts +60 -0
  67. package/src/tools/timer.spec.ts +76 -0
  68. package/src/tools/utils/browserDetection.spec.ts +120 -0
  69. package/src/tools/utils/byteUtils.spec.ts +29 -0
  70. package/src/tools/utils/functionUtils.spec.ts +229 -0
  71. package/src/tools/utils/numberUtils.spec.ts +27 -0
  72. package/src/tools/utils/stringUtils.spec.ts +74 -0
  73. package/src/tools/utils/typeUtils.spec.ts +25 -0
  74. package/src/tools/utils/urlPolyfill.spec.ts +55 -0
  75. package/src/tools/valueHistory.spec.ts +180 -0
  76. package/src/transport/batch.spec.ts +261 -0
  77. package/src/transport/eventBridge.spec.ts +90 -0
  78. package/src/transport/flushController.spec.ts +267 -0
  79. package/src/transport/httpRequest.spec.ts +400 -0
  80. package/src/transport/sendWithRetryStrategy.spec.ts +393 -0
@@ -0,0 +1,20 @@
1
+ import { createHandlingStack } from './handlingStack'
2
+
3
+ describe('createHandlingStack', () => {
4
+ let handlingStack: string
5
+ function internalCall() {
6
+ handlingStack = createHandlingStack('error')
7
+ }
8
+ function userCallTwo() {
9
+ internalCall()
10
+ }
11
+ function userCallOne() {
12
+ userCallTwo()
13
+ }
14
+
15
+ it('should create handling stack trace without internal calls', () => {
16
+ userCallOne()
17
+
18
+ expect(handlingStack).toMatch(/^HandlingStack: error\n\s+at userCallTwo @ (.*)\n\s+at userCallOne @ (.*)/)
19
+ })
20
+ })
@@ -0,0 +1,60 @@
1
+ import { mockClock, mockRequestIdleCallback } from '../../test'
2
+ import { createTaskQueue, MAX_EXECUTION_TIME_ON_TIMEOUT } from './taskQueue'
3
+
4
+ describe('createTaskQueue', () => {
5
+ it('runs the task using an idle callback', () => {
6
+ const requestIdleCallbackMock = mockRequestIdleCallback()
7
+ const taskQueue = createTaskQueue()
8
+ const task = jasmine.createSpy('task')
9
+
10
+ taskQueue.push(task)
11
+ expect(task).not.toHaveBeenCalled()
12
+
13
+ requestIdleCallbackMock.idle()
14
+ expect(task).toHaveBeenCalled()
15
+ })
16
+
17
+ it('runs as many tasks as possible in a single idle callback', () => {
18
+ const clock = mockClock()
19
+ const requestIdleCallbackMock = mockRequestIdleCallback()
20
+
21
+ // Each task takes 10ms to run
22
+ const task1 = jasmine.createSpy().and.callFake(() => clock.tick(10))
23
+ const task2 = jasmine.createSpy().and.callFake(() => clock.tick(10))
24
+ const task3 = jasmine.createSpy().and.callFake(() => clock.tick(10))
25
+
26
+ const taskQueue = createTaskQueue()
27
+
28
+ taskQueue.push(task1)
29
+ taskQueue.push(task2)
30
+ taskQueue.push(task3)
31
+
32
+ requestIdleCallbackMock.idle(15)
33
+ expect(task1).toHaveBeenCalled()
34
+ expect(task2).toHaveBeenCalled()
35
+ expect(task3).not.toHaveBeenCalled()
36
+
37
+ requestIdleCallbackMock.idle(5)
38
+ expect(task3).toHaveBeenCalled()
39
+ })
40
+
41
+ it('runs some tasks in case of timeout', () => {
42
+ const clock = mockClock()
43
+ const requestIdleCallbackMock = mockRequestIdleCallback()
44
+
45
+ const task1 = jasmine.createSpy().and.callFake(() => clock.tick(MAX_EXECUTION_TIME_ON_TIMEOUT - 10))
46
+ const task2 = jasmine.createSpy().and.callFake(() => clock.tick(20))
47
+ const task3 = jasmine.createSpy().and.callFake(() => clock.tick(20))
48
+
49
+ const taskQueue = createTaskQueue()
50
+
51
+ taskQueue.push(task1)
52
+ taskQueue.push(task2)
53
+ taskQueue.push(task3)
54
+
55
+ requestIdleCallbackMock.timeout()
56
+ expect(task1).toHaveBeenCalled()
57
+ expect(task2).toHaveBeenCalled()
58
+ expect(task3).not.toHaveBeenCalled()
59
+ })
60
+ })
@@ -0,0 +1,76 @@
1
+ import { mockClock, mockZoneJs, registerCleanupTask } from '../../test'
2
+ import type { Clock, MockZoneJs } from '../../test'
3
+ import { resetMonitor, startMonitorErrorCollection } from './monitor'
4
+ import { setTimeout, clearTimeout, setInterval, clearInterval } from './timer'
5
+ import { noop } from './utils/functionUtils'
6
+ ;[
7
+ {
8
+ name: 'setTimeout' as const,
9
+ setTimer: setTimeout,
10
+ clearTimer: clearTimeout,
11
+ },
12
+ {
13
+ name: 'setInterval' as const,
14
+ setTimer: setInterval,
15
+ clearTimer: clearInterval,
16
+ },
17
+ ].forEach(({ name, setTimer, clearTimer }) => {
18
+ describe(name, () => {
19
+ let clock: Clock
20
+ let zoneJs: MockZoneJs
21
+
22
+ beforeEach(() => {
23
+ clock = mockClock()
24
+ registerCleanupTask(() => {
25
+ resetMonitor()
26
+ })
27
+ zoneJs = mockZoneJs()
28
+ })
29
+
30
+ it('executes the callback asynchronously', () => {
31
+ const spy = jasmine.createSpy()
32
+ setTimer(spy)
33
+ expect(spy).not.toHaveBeenCalled()
34
+ clock.tick(0)
35
+ expect(spy).toHaveBeenCalledOnceWith()
36
+ })
37
+
38
+ it('schedules an asynchronous task', () => {
39
+ const spy = jasmine.createSpy()
40
+ setTimer(spy)
41
+ expect(spy).not.toHaveBeenCalled()
42
+ clock.tick(0)
43
+ expect(spy).toHaveBeenCalledOnceWith()
44
+ })
45
+
46
+ it('does not use the Zone.js function', () => {
47
+ const zoneJsSetTimerSpy = jasmine.createSpy()
48
+ zoneJs.replaceProperty(window, name, zoneJsSetTimerSpy)
49
+
50
+ setTimer(noop)
51
+ clock.tick(0)
52
+
53
+ expect(zoneJsSetTimerSpy).not.toHaveBeenCalled()
54
+ })
55
+
56
+ it('monitors the callback', () => {
57
+ const onMonitorErrorCollectedSpy = jasmine.createSpy()
58
+ startMonitorErrorCollection(onMonitorErrorCollectedSpy)
59
+
60
+ setTimer(() => {
61
+ throw new Error('foo')
62
+ })
63
+ clock.tick(0)
64
+
65
+ expect(onMonitorErrorCollectedSpy).toHaveBeenCalledOnceWith(new Error('foo'))
66
+ })
67
+
68
+ it('can be canceled', () => {
69
+ const spy = jasmine.createSpy()
70
+ const timerId = setTimer(spy)
71
+ clearTimer(timerId)
72
+ clock.tick(0)
73
+ expect(spy).not.toHaveBeenCalled()
74
+ })
75
+ })
76
+ })
@@ -0,0 +1,120 @@
1
+ import { combine } from '../mergeInto'
2
+ import { Browser, detectBrowser } from './browserDetection'
3
+
4
+ describe('browserDetection', () => {
5
+ it('detects Safari', () => {
6
+ expect(
7
+ detectBrowser(
8
+ fakeWindowWithDefaults({
9
+ navigator: {
10
+ userAgent:
11
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15',
12
+ vendor: 'Apple Computer, Inc.',
13
+ },
14
+ })
15
+ )
16
+ ).toBe(Browser.SAFARI)
17
+
18
+ // Emulates Safari detection if 'navigator.vendor' is removed one day
19
+ expect(
20
+ detectBrowser(
21
+ fakeWindowWithDefaults({
22
+ navigator: {
23
+ userAgent:
24
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15',
25
+ },
26
+ })
27
+ )
28
+ ).toBe(Browser.SAFARI)
29
+
30
+ // Webview on iOS
31
+ expect(
32
+ detectBrowser(
33
+ fakeWindowWithDefaults({
34
+ navigator: {
35
+ userAgent:
36
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/20B110 [FBAN/FBIOS;FBDV/iPhone14,5;FBMD/iPhone;FBSN/iOS;FBSV/16.1.2;FBSS/3;FBID/phone;FBLC/en_US;FBOP/5]',
37
+ vendor: 'Apple Computer, Inc.',
38
+ },
39
+ })
40
+ )
41
+ ).toBe(Browser.SAFARI)
42
+ })
43
+
44
+ it('detects Chromium', () => {
45
+ // Google Chrome 118
46
+ expect(
47
+ detectBrowser(
48
+ fakeWindowWithDefaults({
49
+ navigator: {
50
+ userAgent:
51
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
52
+ vendor: 'Google Inc.',
53
+ },
54
+ chrome: {},
55
+ })
56
+ )
57
+ ).toBe(Browser.CHROMIUM)
58
+
59
+ // Headless chrome
60
+ expect(
61
+ detectBrowser(
62
+ fakeWindowWithDefaults({
63
+ navigator: {
64
+ userAgent:
65
+ 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/92.0.4512.0 Safari/537.36',
66
+ vendor: 'Google Inc.',
67
+ },
68
+ })
69
+ )
70
+ ).toBe(Browser.CHROMIUM)
71
+
72
+ // Microsoft Edge 89
73
+ expect(
74
+ detectBrowser(
75
+ fakeWindowWithDefaults({
76
+ navigator: {
77
+ userAgent:
78
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36 Edg/89.0.774.54',
79
+ vendor: 'Google Inc.',
80
+ },
81
+ chrome: {},
82
+ })
83
+ )
84
+ ).toBe(Browser.CHROMIUM)
85
+ })
86
+
87
+ it('other browsers', () => {
88
+ // Firefox 10
89
+ expect(
90
+ detectBrowser(
91
+ fakeWindowWithDefaults({
92
+ navigator: { userAgent: 'Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0' },
93
+ })
94
+ )
95
+ ).toBe(Browser.OTHER)
96
+
97
+ // Firefox 120
98
+ expect(
99
+ detectBrowser(
100
+ fakeWindowWithDefaults({
101
+ navigator: {
102
+ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:120.0) Gecko/20100101 Firefox/120.0',
103
+ },
104
+ })
105
+ )
106
+ ).toBe(Browser.OTHER)
107
+ })
108
+
109
+ function fakeWindowWithDefaults(partial: any): Window {
110
+ return combine(
111
+ {
112
+ navigator: {
113
+ userAgent: '',
114
+ },
115
+ document: {},
116
+ },
117
+ partial
118
+ ) as Window
119
+ }
120
+ })
@@ -0,0 +1,29 @@
1
+ import { computeBytesCount, concatBuffers } from './byteUtils'
2
+
3
+ describe('byteUtils', () => {
4
+ describe('computeBytesCount', () => {
5
+ it('should count the bytes of a message composed of 1 byte characters', () => {
6
+ expect(computeBytesCount('1234')).toEqual(4)
7
+ })
8
+
9
+ it('should count the bytes of a message composed of multiple bytes characters', () => {
10
+ expect(computeBytesCount('🪐')).toEqual(4)
11
+ })
12
+ })
13
+
14
+ describe('concatBuffers', () => {
15
+ it('concatenates buffers correctly', () => {
16
+ expect(concatBuffers([new Uint8Array([1, 2]), new Uint8Array([3, 4, 5]), new Uint8Array([6])])).toEqual(
17
+ new Uint8Array([1, 2, 3, 4, 5, 6])
18
+ )
19
+ })
20
+
21
+ it('concatenates an empty buffer and a non-empty buffer', () => {
22
+ expect(concatBuffers([new Uint8Array([]), new Uint8Array([1, 2, 3])])).toEqual(new Uint8Array([1, 2, 3]))
23
+ })
24
+
25
+ it('returns an empty buffer when an empty array is passed', () => {
26
+ expect(concatBuffers([])).toEqual(new Uint8Array([]))
27
+ })
28
+ })
29
+ })
@@ -0,0 +1,229 @@
1
+ import type { Clock } from '../../../test'
2
+ import { mockClock } from '../../../test'
3
+ import { throttle } from './functionUtils'
4
+
5
+ describe('functionUtils', () => {
6
+ describe('throttle', () => {
7
+ let spy: jasmine.Spy
8
+ let throttled: () => void
9
+ let cancel: () => void
10
+ let clock: Clock
11
+
12
+ beforeEach(() => {
13
+ clock = mockClock()
14
+ spy = jasmine.createSpy()
15
+ })
16
+
17
+ describe('when {leading: false, trailing:false}', () => {
18
+ beforeEach(() => {
19
+ throttled = throttle(spy, 2, { leading: false, trailing: false }).throttled
20
+ })
21
+
22
+ it('should not call throttled function', () => {
23
+ throttled()
24
+ expect(spy).toHaveBeenCalledTimes(0)
25
+ clock.tick(2)
26
+ expect(spy).toHaveBeenCalledTimes(0)
27
+ })
28
+
29
+ it('should not called throttled function after the wait period', () => {
30
+ throttled()
31
+ expect(spy).toHaveBeenCalledTimes(0)
32
+
33
+ clock.tick(1)
34
+ expect(spy).toHaveBeenCalledTimes(0)
35
+
36
+ throttled()
37
+ expect(spy).toHaveBeenCalledTimes(0)
38
+
39
+ clock.tick(1)
40
+ expect(spy).toHaveBeenCalledTimes(0)
41
+
42
+ throttled()
43
+ expect(spy).toHaveBeenCalledTimes(0)
44
+
45
+ clock.tick(1)
46
+ expect(spy).toHaveBeenCalledTimes(0)
47
+
48
+ clock.tick(1)
49
+ expect(spy).toHaveBeenCalledTimes(0)
50
+ })
51
+
52
+ it('should not called throttled function performed after the wait period', () => {
53
+ throttled()
54
+ clock.tick(2)
55
+ throttled()
56
+ clock.tick(2)
57
+ expect(spy).toHaveBeenCalledTimes(0)
58
+ })
59
+ })
60
+
61
+ describe('when {leading: false, trailing:true}', () => {
62
+ beforeEach(() => {
63
+ throttled = throttle(spy, 2, { leading: false }).throttled
64
+ })
65
+
66
+ it('should call throttled function after the wait period', () => {
67
+ throttled()
68
+ expect(spy).toHaveBeenCalledTimes(0)
69
+ clock.tick(2)
70
+ expect(spy).toHaveBeenCalledTimes(1)
71
+ })
72
+
73
+ it('should dismiss calls made during the wait period', () => {
74
+ throttled()
75
+ expect(spy).toHaveBeenCalledTimes(0)
76
+
77
+ clock.tick(1)
78
+ expect(spy).toHaveBeenCalledTimes(0)
79
+
80
+ throttled()
81
+ expect(spy).toHaveBeenCalledTimes(0)
82
+
83
+ clock.tick(1)
84
+ expect(spy).toHaveBeenCalledTimes(1)
85
+
86
+ throttled()
87
+ expect(spy).toHaveBeenCalledTimes(1)
88
+
89
+ clock.tick(1)
90
+ expect(spy).toHaveBeenCalledTimes(1)
91
+
92
+ clock.tick(1)
93
+ expect(spy).toHaveBeenCalledTimes(2)
94
+ })
95
+
96
+ it('should perform calls made after the wait period', () => {
97
+ throttled()
98
+ clock.tick(2)
99
+ throttled()
100
+ clock.tick(2)
101
+ expect(spy).toHaveBeenCalledTimes(2)
102
+ })
103
+ })
104
+
105
+ describe('when {leading: true, trailing:false}', () => {
106
+ beforeEach(() => {
107
+ throttled = throttle(spy, 2, { trailing: false }).throttled
108
+ })
109
+
110
+ it('should call throttled function immediately', () => {
111
+ throttled()
112
+ expect(spy).toHaveBeenCalledTimes(1)
113
+ clock.tick(2)
114
+ expect(spy).toHaveBeenCalledTimes(1)
115
+ })
116
+
117
+ it('should dismiss calls made during the wait period', () => {
118
+ throttled()
119
+ expect(spy).toHaveBeenCalledTimes(1)
120
+
121
+ clock.tick(1)
122
+ expect(spy).toHaveBeenCalledTimes(1)
123
+
124
+ throttled()
125
+ expect(spy).toHaveBeenCalledTimes(1)
126
+
127
+ clock.tick(1)
128
+ expect(spy).toHaveBeenCalledTimes(1)
129
+
130
+ throttled()
131
+ expect(spy).toHaveBeenCalledTimes(2)
132
+
133
+ clock.tick(1)
134
+ expect(spy).toHaveBeenCalledTimes(2)
135
+
136
+ clock.tick(1)
137
+ expect(spy).toHaveBeenCalledTimes(2)
138
+ })
139
+
140
+ it('should perform calls made after the wait period', () => {
141
+ throttled()
142
+ clock.tick(2)
143
+ throttled()
144
+ clock.tick(2)
145
+ expect(spy).toHaveBeenCalledTimes(2)
146
+ })
147
+ })
148
+
149
+ describe('when {leading: true, trailing:true}', () => {
150
+ beforeEach(() => {
151
+ throttled = throttle(spy, 2).throttled
152
+ })
153
+
154
+ it('should call throttled function immediately', () => {
155
+ throttled()
156
+ expect(spy).toHaveBeenCalledTimes(1)
157
+ clock.tick(2)
158
+ expect(spy).toHaveBeenCalledTimes(1)
159
+ })
160
+
161
+ it('should postpone calls made during the wait period to after the period', () => {
162
+ throttled()
163
+ expect(spy).toHaveBeenCalledTimes(1)
164
+
165
+ clock.tick(1)
166
+ expect(spy).toHaveBeenCalledTimes(1)
167
+
168
+ throttled()
169
+ expect(spy).toHaveBeenCalledTimes(1)
170
+
171
+ clock.tick(1)
172
+ expect(spy).toHaveBeenCalledTimes(2)
173
+
174
+ throttled()
175
+ expect(spy).toHaveBeenCalledTimes(3)
176
+
177
+ clock.tick(1)
178
+ expect(spy).toHaveBeenCalledTimes(3)
179
+
180
+ clock.tick(1)
181
+ expect(spy).toHaveBeenCalledTimes(3)
182
+ })
183
+
184
+ it('should perform calls made after the wait period', () => {
185
+ throttled()
186
+ clock.tick(2)
187
+ throttled()
188
+ clock.tick(2)
189
+ expect(spy).toHaveBeenCalledTimes(2)
190
+ })
191
+ })
192
+
193
+ describe('cancel', () => {
194
+ beforeEach(() => {
195
+ const result = throttle(spy, 2)
196
+ cancel = result.cancel
197
+ throttled = result.throttled
198
+ })
199
+
200
+ it('should abort pending execution', () => {
201
+ throttled()
202
+ throttled()
203
+ expect(spy).toHaveBeenCalledTimes(1)
204
+
205
+ cancel()
206
+
207
+ clock.tick(2)
208
+ expect(spy).toHaveBeenCalledTimes(1)
209
+ })
210
+
211
+ it('should allow future calls', () => {
212
+ cancel()
213
+ throttled()
214
+ expect(spy).toHaveBeenCalledTimes(1)
215
+ clock.tick(2)
216
+ expect(spy).toHaveBeenCalledTimes(1)
217
+ })
218
+ })
219
+
220
+ it('passes last parameters as arguments', () => {
221
+ const throttled = throttle(spy, 2).throttled
222
+ throttled(1)
223
+ throttled(2)
224
+ throttled(3)
225
+ clock.tick(2)
226
+ expect(spy.calls.allArgs()).toEqual([[1], [3]])
227
+ })
228
+ })
229
+ })
@@ -0,0 +1,27 @@
1
+ import { performDraw, round } from './numberUtils'
2
+
3
+ describe('numberUtils', () => {
4
+ it('should perform a draw', () => {
5
+ let random = 0
6
+ spyOn(Math, 'random').and.callFake(() => random)
7
+
8
+ expect(performDraw(0)).toBe(false)
9
+ expect(performDraw(100)).toEqual(true)
10
+
11
+ random = 1
12
+ expect(performDraw(100)).toEqual(true)
13
+
14
+ random = 0.0001
15
+ expect(performDraw(0.01)).toEqual(true)
16
+
17
+ random = 0.1
18
+ expect(performDraw(0.01)).toEqual(false)
19
+ })
20
+
21
+ it('should round', () => {
22
+ expect(round(10.12591, 0)).toEqual(10)
23
+ expect(round(10.12591, 1)).toEqual(10.1)
24
+ expect(round(10.12591, 2)).toEqual(10.13)
25
+ expect(round(10.12591, 3)).toEqual(10.126)
26
+ })
27
+ })
@@ -0,0 +1,74 @@
1
+ import {
2
+ safeTruncate,
3
+ findCommaSeparatedValue,
4
+ findCommaSeparatedValues,
5
+ findAllCommaSeparatedValues,
6
+ } from './stringUtils'
7
+
8
+ describe('stringUtils', () => {
9
+ describe('safeTruncate', () => {
10
+ it('should truncate a string', () => {
11
+ const truncated = safeTruncate('1234😎7890', 6)
12
+ expect(truncated.length).toBe(6)
13
+ expect(truncated).toBe('1234😎')
14
+ })
15
+
16
+ it('should not break a surrogate characters pair', () => {
17
+ const truncated = safeTruncate('12345😎890', 6)
18
+ expect(truncated.length).toBe(7)
19
+ expect(truncated).toBe('12345😎')
20
+ })
21
+
22
+ it('should add the suffix when the string is truncated', () => {
23
+ const truncated = safeTruncate('12345😎890', 6, '...')
24
+ expect(truncated).toBe('12345😎...')
25
+ })
26
+
27
+ it('should not add the suffix when the string is not truncated', () => {
28
+ const truncated = safeTruncate('1234😎', 5, '...')
29
+ expect(truncated).toBe('1234😎')
30
+ })
31
+ })
32
+
33
+ describe('findCommaSeparatedValue', () => {
34
+ it('returns the value from a comma separated hash', () => {
35
+ expect(findCommaSeparatedValue('foo=a;bar=b', 'foo')).toBe('a')
36
+ expect(findCommaSeparatedValue('foo=a;bar=b', 'bar')).toBe('b')
37
+ })
38
+
39
+ it('is white-spaces tolerant', () => {
40
+ expect(findCommaSeparatedValue(' foo = a; bar = b', 'foo')).toBe('a')
41
+ expect(findCommaSeparatedValue(' foo = a; bar = b', 'bar')).toBe('b')
42
+ })
43
+
44
+ it('supports values containing an = character', () => {
45
+ expect(findCommaSeparatedValue('foo=a=b', 'foo')).toBe('a=b')
46
+ })
47
+
48
+ it('supports keys containing `-`', () => {
49
+ expect(findCommaSeparatedValue('foo-bar=baz', 'foo-bar')).toBe('baz')
50
+ })
51
+
52
+ it('returns undefined if the value is not found', () => {
53
+ expect(findCommaSeparatedValue('foo=a;bar=b', 'baz')).toBe(undefined)
54
+ })
55
+ })
56
+
57
+ describe('findCommaSeparatedValues', () => {
58
+ it('returns the values from a comma separated hash', () => {
59
+ const expectedValues = new Map<string, string>()
60
+ expectedValues.set('foo', 'a')
61
+ expectedValues.set('bar', 'b')
62
+ expect(findCommaSeparatedValues('foo=a;bar=b')).toEqual(expectedValues)
63
+ })
64
+ })
65
+
66
+ describe('findAllCommaSeparatedValues', () => {
67
+ it('returns all the values from a comma separated hash', () => {
68
+ const expectedValues = new Map<string, string[]>()
69
+ expectedValues.set('foo', ['a', 'c'])
70
+ expectedValues.set('bar', ['b'])
71
+ expect(findAllCommaSeparatedValues('foo=a;bar=b;foo=c')).toEqual(expectedValues)
72
+ })
73
+ })
74
+ })
@@ -0,0 +1,25 @@
1
+ import { getType } from './typeUtils'
2
+
3
+ describe('typeUtils', () => {
4
+ describe('getType', () => {
5
+ it('should return "null" for null value', () => {
6
+ expect(getType(null)).toEqual('null')
7
+ expect(getType(undefined)).not.toEqual('null')
8
+ })
9
+
10
+ it('should return "array" for array value', () => {
11
+ expect(getType([])).toEqual('array')
12
+ expect(getType([1, 2, 3])).toEqual('array')
13
+ expect(getType([1, 2, [3, 4, 5]])).toEqual('array')
14
+ })
15
+
16
+ it('should return result of typeof operator for other types', () => {
17
+ expect(getType({})).toEqual('object')
18
+ expect(getType(() => null)).toEqual('function')
19
+ expect(getType('test')).toEqual('string')
20
+ expect(getType(1)).toEqual('number')
21
+ expect(getType(false)).toEqual('boolean')
22
+ expect(getType(new Date())).toEqual('object')
23
+ })
24
+ })
25
+ })