@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,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
|
+
})
|