@tma.js/sdk 1.3.0 → 1.4.1
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/dist/dts/index.d.ts +1 -0
- package/dist/dts/launch-params/index.d.ts +1 -0
- package/dist/dts/launch-params/retrieveFromUrl.d.ts +6 -0
- package/dist/dts/launch-params/types.d.ts +12 -8
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.iife.js +1 -1
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +253 -238
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/globals.ts +39 -0
- package/src/back-button/__tests__/BackButton.ts +129 -0
- package/src/bridge/__tests__/request.ts +236 -0
- package/src/bridge/env/__tests__/hasExternalNotify.ts +15 -0
- package/src/bridge/env/__tests__/hasWebviewProxy.ts +15 -0
- package/src/bridge/env/__tests__/isIframe.ts +30 -0
- package/src/bridge/events/__tests__/createEmitter.ts +143 -0
- package/src/bridge/events/__tests__/off.ts +34 -0
- package/src/bridge/events/__tests__/on.ts +49 -0
- package/src/bridge/events/__tests__/onTelegramEvent.ts +51 -0
- package/src/bridge/events/__tests__/once.ts +64 -0
- package/src/bridge/events/__tests__/singletonEmitter.ts +22 -0
- package/src/bridge/events/__tests__/subscribe.ts +49 -0
- package/src/bridge/events/__tests__/unsubscribe.ts +34 -0
- package/src/bridge/events/parsers/__tests__/clipboardTextReceived.ts +21 -0
- package/src/bridge/events/parsers/__tests__/invoiceClosed.ts +12 -0
- package/src/bridge/events/parsers/__tests__/popupClosed.ts +10 -0
- package/src/bridge/events/parsers/__tests__/qrTextReceived.ts +9 -0
- package/src/bridge/events/parsers/__tests__/theme-changed.ts +42 -0
- package/src/bridge/events/parsers/__tests__/viewportChanged.ts +49 -0
- package/src/bridge/methods/__tests__/createPostEvent.ts +37 -0
- package/src/bridge/methods/__tests__/postEvent.ts +137 -0
- package/src/classnames/__tests__/classNames.ts +20 -0
- package/src/classnames/__tests__/mergeClassNames.ts +21 -0
- package/src/closing-behavior/__tests__/ClosingBehavior.ts +86 -0
- package/src/colors/__tests__/isColorDark.ts +12 -0
- package/src/colors/__tests__/isRGB.ts +13 -0
- package/src/colors/__tests__/isRGBShort.ts +13 -0
- package/src/colors/__tests__/toRGB.ts +23 -0
- package/src/event-emitter/__tests__/EventEmitter.ts +145 -0
- package/src/haptic-feedback/__tests__/HapticFeedback.ts +68 -0
- package/src/index.ts +12 -0
- package/src/init/creators/__tests__/createViewport.ts +96 -0
- package/src/init-data/__tests__/InitData.ts +98 -0
- package/src/init-data/__tests__/chatParser.ts +102 -0
- package/src/init-data/__tests__/initDataParser.ts +136 -0
- package/src/init-data/__tests__/parseInitData.ts +136 -0
- package/src/init-data/__tests__/userParser.ts +96 -0
- package/src/launch-params/__tests__/retrieveFromUrl.ts +19 -0
- package/src/launch-params/index.ts +1 -0
- package/src/launch-params/launchParamsParser.ts +4 -0
- package/src/launch-params/retrieveFromLocation.ts +2 -2
- package/src/launch-params/retrieveFromPerformance.ts +2 -7
- package/src/launch-params/retrieveFromUrl.ts +19 -0
- package/src/launch-params/types.ts +13 -8
- package/src/logger/__tests__/Logger.ts +107 -0
- package/src/main-button/__tests__/MainButton.ts +346 -0
- package/src/mini-app/__tests__/MiniApp.ts +140 -0
- package/src/misc/__tests__/isRecord.ts +21 -0
- package/src/navigation/HashNavigator/__tests__/HashNavigator.ts +144 -0
- package/src/navigation/HashNavigator/__tests__/drop.ts +42 -0
- package/src/navigation/HashNavigator/__tests__/go.ts +9 -0
- package/src/parsing/__tests__/ArrayValueParser.ts +18 -0
- package/src/parsing/__tests__/toRecord.ts +10 -0
- package/src/parsing/parsers/__tests__/array.ts +39 -0
- package/src/parsing/parsers/__tests__/boolean.ts +31 -0
- package/src/parsing/parsers/__tests__/date.ts +25 -0
- package/src/parsing/parsers/__tests__/json.ts +80 -0
- package/src/parsing/parsers/__tests__/number.ts +23 -0
- package/src/parsing/parsers/__tests__/rgb.ts +22 -0
- package/src/parsing/parsers/__tests__/searchParams.ts +105 -0
- package/src/parsing/parsers/__tests__/string.ts +25 -0
- package/src/popup/__tests__/Popup.ts +130 -0
- package/src/popup/__tests__/preparePopupParams.ts +85 -0
- package/src/supports/__tests__/supports.ts +123 -0
- package/src/theme-params/__tests__/keys.ts +19 -0
- package/src/theme-params/__tests__/parseThemeParams.ts +29 -0
- package/src/theme-params/__tests__/serializeThemeParams.ts +29 -0
- package/src/theme-params/__tests__/themeParamsParser.ts +29 -0
- package/src/timeout/__tests__/isTimeoutError.ts +9 -0
- package/src/timeout/__tests__/withTimeout.ts +28 -0
- package/src/version/__tests__/compareVersions.ts +19 -0
- package/src/viewport/__tests__/isStableViewportPlatform.ts +15 -0
- package/src/viewport/__tests__/utils.ts +12 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { ClosingBehavior } from '../ClosingBehavior';
|
|
4
|
+
|
|
5
|
+
describe('disable', () => {
|
|
6
|
+
it('should call "web_app_setup_closing_behavior" method with "need_confirmation" equal to false', () => {
|
|
7
|
+
const postEvent = vi.fn();
|
|
8
|
+
const confirmation = new ClosingBehavior(true, postEvent);
|
|
9
|
+
|
|
10
|
+
expect(postEvent).toHaveBeenCalledTimes(0);
|
|
11
|
+
confirmation.disableConfirmation();
|
|
12
|
+
expect(postEvent).toHaveBeenCalledTimes(1);
|
|
13
|
+
expect(postEvent).toHaveBeenCalledWith('web_app_setup_closing_behavior', { need_confirmation: false });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should emit "isConfirmationNeededChanged" event with false value', () => {
|
|
17
|
+
const confirmation = new ClosingBehavior(true, vi.fn());
|
|
18
|
+
const listener = vi.fn();
|
|
19
|
+
|
|
20
|
+
confirmation.on('change:isConfirmationNeeded', listener);
|
|
21
|
+
expect(listener).toHaveBeenCalledTimes(0);
|
|
22
|
+
confirmation.disableConfirmation();
|
|
23
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
24
|
+
expect(listener).toHaveBeenCalledWith(false);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('enable', () => {
|
|
29
|
+
it('should call "web_app_setup_closing_behavior" method with "need_confirmation" equal to true', () => {
|
|
30
|
+
const postEvent = vi.fn();
|
|
31
|
+
const confirmation = new ClosingBehavior(false, postEvent);
|
|
32
|
+
|
|
33
|
+
expect(postEvent).toHaveBeenCalledTimes(0);
|
|
34
|
+
confirmation.enableConfirmation();
|
|
35
|
+
expect(postEvent).toHaveBeenCalledTimes(1);
|
|
36
|
+
expect(postEvent).toHaveBeenCalledWith('web_app_setup_closing_behavior', { need_confirmation: true });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should emit "isConfirmationNeededChanged" event with true value', () => {
|
|
40
|
+
const confirmation = new ClosingBehavior(false, vi.fn());
|
|
41
|
+
const listener = vi.fn();
|
|
42
|
+
|
|
43
|
+
confirmation.on('change:isConfirmationNeeded', listener);
|
|
44
|
+
expect(listener).toHaveBeenCalledTimes(0);
|
|
45
|
+
confirmation.enableConfirmation();
|
|
46
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
47
|
+
expect(listener).toHaveBeenCalledWith(true);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('on', () => {
|
|
52
|
+
describe('"isConfirmationNeededChanged" event', () => {
|
|
53
|
+
it('should add event listener to event', () => {
|
|
54
|
+
const listener = vi.fn();
|
|
55
|
+
const confirmation = new ClosingBehavior(false, vi.fn());
|
|
56
|
+
|
|
57
|
+
confirmation.on('change:isConfirmationNeeded', listener);
|
|
58
|
+
|
|
59
|
+
expect(listener).toHaveBeenCalledTimes(0);
|
|
60
|
+
confirmation.enableConfirmation();
|
|
61
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('off', () => {
|
|
67
|
+
describe('"isConfirmationNeededChanged" event', () => {
|
|
68
|
+
it('should remove event listener from event', () => {
|
|
69
|
+
const listener = vi.fn();
|
|
70
|
+
const confirmation = new ClosingBehavior(false, vi.fn());
|
|
71
|
+
|
|
72
|
+
confirmation.on('change:isConfirmationNeeded', listener);
|
|
73
|
+
|
|
74
|
+
expect(listener).toHaveBeenCalledTimes(0);
|
|
75
|
+
confirmation.enableConfirmation();
|
|
76
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
77
|
+
|
|
78
|
+
confirmation.off('change:isConfirmationNeeded', listener);
|
|
79
|
+
listener.mockClear();
|
|
80
|
+
|
|
81
|
+
expect(listener).toHaveBeenCalledTimes(0);
|
|
82
|
+
confirmation.disableConfirmation();
|
|
83
|
+
expect(listener).toHaveBeenCalledTimes(0);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { isColorDark } from '../isColorDark';
|
|
4
|
+
|
|
5
|
+
it('should return true in case, the value which is equal to Math.sqrt(0.299 * R * R + 0.587 * G * G + 0.114 * B * B) is less than 120 and false otherwise', () => {
|
|
6
|
+
expect(isColorDark('#17212b')).toBe(true);
|
|
7
|
+
expect(isColorDark('#f5f5f5')).toBe(false);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should throw an error in case, passed value has not convertable to RGB format', () => {
|
|
11
|
+
expect(() => isColorDark('abc')).toThrow();
|
|
12
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { isRGB } from '../isRGB';
|
|
4
|
+
|
|
5
|
+
it('should return true for correct full RGB representation', () => {
|
|
6
|
+
expect(isRGB('#ffffff')).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('should return false for any other value', () => {
|
|
10
|
+
['abc', '#ffff', '#fff', '#fffffg'].forEach((v) => {
|
|
11
|
+
expect(isRGB(v)).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { isRGBShort } from '../isRGBShort';
|
|
4
|
+
|
|
5
|
+
it('should return true for correct short RGB representation', () => {
|
|
6
|
+
expect(isRGBShort('#fff')).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('should return false for any other value', () => {
|
|
10
|
+
['abc', '#ffff', '#ffffff', '#ggg'].forEach((v) => {
|
|
11
|
+
expect(isRGBShort(v)).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { toRGB } from '../toRGB';
|
|
4
|
+
|
|
5
|
+
it('should return same value in case, full version of RGB is passed', () => {
|
|
6
|
+
expect(toRGB('#ffffff')).toBe('#ffffff');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('should return full RGB value in case, its short presentation is passed', () => {
|
|
10
|
+
expect(toRGB('#abc')).toBe('#aabbcc');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should return RGB representation of rgb(*,*,*) pattern', () => {
|
|
14
|
+
expect(toRGB('rgb(6,56,11)')).toBe('#06380b');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return RGB representation of rgba(*,*,*) pattern', () => {
|
|
18
|
+
expect(toRGB('rgba(6,56,11,22)')).toBe('#06380b');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should throw an error in other cases', () => {
|
|
22
|
+
expect(() => toRGB('abc')).toThrow();
|
|
23
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { EventEmitter } from '../EventEmitter';
|
|
4
|
+
|
|
5
|
+
interface EventsMap {
|
|
6
|
+
test: (a: number, b: boolean) => void;
|
|
7
|
+
hey: never;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let ee: EventEmitter<EventsMap>;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
ee = new EventEmitter<EventsMap>();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('on', () => {
|
|
17
|
+
it('should emit bound listener with specified arguments', () => {
|
|
18
|
+
const listener = vi.fn();
|
|
19
|
+
ee.on('test', listener);
|
|
20
|
+
ee.emit('test', 1, true);
|
|
21
|
+
expect(listener).toHaveBeenCalledOnce();
|
|
22
|
+
expect(listener).toBeCalledWith(1, true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should emit bound listener with specified arguments as many times as it was bound', () => {
|
|
26
|
+
const listener = vi.fn();
|
|
27
|
+
ee.on('test', listener);
|
|
28
|
+
ee.on('test', listener);
|
|
29
|
+
ee.emit('test', 1, true);
|
|
30
|
+
expect(listener).toHaveBeenCalledTimes(2);
|
|
31
|
+
expect(listener).toHaveBeenNthCalledWith(1, 1, true);
|
|
32
|
+
expect(listener).toHaveBeenNthCalledWith(2, 1, true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should not emit bound listener in case, event name does not match', () => {
|
|
36
|
+
const listener = vi.fn();
|
|
37
|
+
ee.on('test', listener);
|
|
38
|
+
ee.emit('hey');
|
|
39
|
+
expect(listener).not.toBeCalled();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should remove listener if returned function was called', () => {
|
|
43
|
+
const listener = vi.fn();
|
|
44
|
+
const off = ee.on('test', listener);
|
|
45
|
+
|
|
46
|
+
off();
|
|
47
|
+
ee.emit('test', 1, true);
|
|
48
|
+
expect(listener).not.toBeCalled();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('once', () => {
|
|
53
|
+
it('should emit bound listener with specified arguments only once', () => {
|
|
54
|
+
const listener = vi.fn();
|
|
55
|
+
ee.once('test', listener);
|
|
56
|
+
ee.emit('test', 1, true);
|
|
57
|
+
ee.emit('test', 1, true);
|
|
58
|
+
ee.emit('test', 1, true);
|
|
59
|
+
expect(listener).toHaveBeenCalledOnce();
|
|
60
|
+
expect(listener).toBeCalledWith(1, true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should emit bound listener with specified arguments as many times as it was bound', () => {
|
|
64
|
+
const listener = vi.fn();
|
|
65
|
+
ee.once('test', listener);
|
|
66
|
+
ee.once('test', listener);
|
|
67
|
+
ee.emit('test', 1, true);
|
|
68
|
+
ee.emit('test', 1, true);
|
|
69
|
+
expect(listener).toHaveBeenCalledTimes(2);
|
|
70
|
+
expect(listener).toHaveBeenNthCalledWith(1, 1, true);
|
|
71
|
+
expect(listener).toHaveBeenNthCalledWith(2, 1, true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should not emit bound listener in case, event name does not match', () => {
|
|
75
|
+
const listener = vi.fn();
|
|
76
|
+
ee.once('test', listener);
|
|
77
|
+
ee.emit('hey');
|
|
78
|
+
expect(listener).not.toBeCalled();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should remove listener if returned function was called', () => {
|
|
82
|
+
const listener = vi.fn();
|
|
83
|
+
const off = ee.once('test', listener);
|
|
84
|
+
|
|
85
|
+
off();
|
|
86
|
+
ee.emit('test', 1, true);
|
|
87
|
+
expect(listener).not.toBeCalled();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('off', () => {
|
|
92
|
+
it('should not emit bound listener in case, it was unbound', () => {
|
|
93
|
+
const listener = vi.fn();
|
|
94
|
+
ee.on('test', listener);
|
|
95
|
+
ee.off('test', listener);
|
|
96
|
+
ee.emit('test', 1, true);
|
|
97
|
+
expect(listener).not.toBeCalled();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should not do anything in case, event has no listeners', () => {
|
|
101
|
+
expect(() => {
|
|
102
|
+
const listener = vi.fn();
|
|
103
|
+
ee.off('test', listener);
|
|
104
|
+
}).not.toThrow();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should remove event listener bound via "once" method', () => {
|
|
108
|
+
const listener = vi.fn();
|
|
109
|
+
ee.once('test', listener);
|
|
110
|
+
ee.off('test', listener);
|
|
111
|
+
ee.emit('test', 1, true);
|
|
112
|
+
expect(listener).not.toBeCalled();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should not do anything if received not bound listener', () => {
|
|
116
|
+
ee.on('test', vi.fn());
|
|
117
|
+
expect(() => ee.off('test', vi.fn())).not.toThrow();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('subscribe', () => {
|
|
122
|
+
it('should catch any emitted event', () => {
|
|
123
|
+
const listener = vi.fn();
|
|
124
|
+
ee.subscribe(listener);
|
|
125
|
+
ee.emit('test', 1, true);
|
|
126
|
+
ee.emit('hey');
|
|
127
|
+
expect(listener).toBeCalledTimes(2);
|
|
128
|
+
expect(listener).toHaveBeenNthCalledWith(1, 'test', 1, true);
|
|
129
|
+
expect(listener).toHaveBeenNthCalledWith(2, 'hey');
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('unsubscribe', () => {
|
|
134
|
+
it('should not emit event if it was unbound', () => {
|
|
135
|
+
const listener = vi.fn();
|
|
136
|
+
ee.subscribe(listener);
|
|
137
|
+
ee.unsubscribe(listener);
|
|
138
|
+
ee.emit('test', 1, true);
|
|
139
|
+
expect(listener).not.toBeCalled();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should not do anything if received not bound listener', () => {
|
|
143
|
+
expect(() => ee.unsubscribe(vi.fn())).not.toThrow();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { HapticFeedback } from '../HapticFeedback';
|
|
4
|
+
|
|
5
|
+
describe('impactOccurred', () => {
|
|
6
|
+
it('should call "web_app_trigger_haptic_feedback" method with { type: "impact", style: {{style}} }', () => {
|
|
7
|
+
const postEvent = vi.fn();
|
|
8
|
+
const haptic = new HapticFeedback('', postEvent);
|
|
9
|
+
|
|
10
|
+
expect(postEvent).toHaveBeenCalledTimes(0);
|
|
11
|
+
haptic.impactOccurred('heavy');
|
|
12
|
+
expect(postEvent).toHaveBeenCalledTimes(1);
|
|
13
|
+
expect(postEvent).toHaveBeenCalledWith('web_app_trigger_haptic_feedback', {
|
|
14
|
+
type: 'impact',
|
|
15
|
+
impact_style: 'heavy',
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('notificationOccurred', () => {
|
|
21
|
+
it('should call "web_app_trigger_haptic_feedback" method with { type: "notification", notification_type: {{type}} }', () => {
|
|
22
|
+
const postEvent = vi.fn();
|
|
23
|
+
const haptic = new HapticFeedback('', postEvent);
|
|
24
|
+
|
|
25
|
+
expect(postEvent).toHaveBeenCalledTimes(0);
|
|
26
|
+
haptic.notificationOccurred('success');
|
|
27
|
+
expect(postEvent).toHaveBeenCalledTimes(1);
|
|
28
|
+
expect(postEvent).toHaveBeenCalledWith('web_app_trigger_haptic_feedback', {
|
|
29
|
+
type: 'notification',
|
|
30
|
+
notification_type: 'success',
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('selectionChanged', () => {
|
|
36
|
+
it('should call "web_app_trigger_haptic_feedback" method with { type: "selection_change" }', () => {
|
|
37
|
+
const postEvent = vi.fn();
|
|
38
|
+
const haptic = new HapticFeedback('', postEvent);
|
|
39
|
+
|
|
40
|
+
expect(postEvent).toHaveBeenCalledTimes(0);
|
|
41
|
+
haptic.selectionChanged();
|
|
42
|
+
expect(postEvent).toHaveBeenCalledTimes(1);
|
|
43
|
+
expect(postEvent).toHaveBeenCalledWith('web_app_trigger_haptic_feedback', {
|
|
44
|
+
type: 'selection_change',
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('supports', () => {
|
|
50
|
+
describe('impactOccurred / notificationOccurred / selectionChanged', () => {
|
|
51
|
+
it('should return true in case, HapticFeedback version is 6.1 or higher. False, otherwise', () => {
|
|
52
|
+
const haptic1 = new HapticFeedback('6.0');
|
|
53
|
+
expect(haptic1.supports('impactOccurred')).toBe(false);
|
|
54
|
+
expect(haptic1.supports('notificationOccurred')).toBe(false);
|
|
55
|
+
expect(haptic1.supports('selectionChanged')).toBe(false);
|
|
56
|
+
|
|
57
|
+
const haptic2 = new HapticFeedback('6.1');
|
|
58
|
+
expect(haptic2.supports('impactOccurred')).toBe(true);
|
|
59
|
+
expect(haptic2.supports('notificationOccurred')).toBe(true);
|
|
60
|
+
expect(haptic2.supports('selectionChanged')).toBe(true);
|
|
61
|
+
|
|
62
|
+
const haptic3 = new HapticFeedback('6.2');
|
|
63
|
+
expect(haptic3.supports('impactOccurred')).toBe(true);
|
|
64
|
+
expect(haptic3.supports('notificationOccurred')).toBe(true);
|
|
65
|
+
expect(haptic3.supports('selectionChanged')).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -116,6 +116,18 @@ export {
|
|
|
116
116
|
type HashNavigatorEventListener,
|
|
117
117
|
type HashNavigatorEventName,
|
|
118
118
|
} from './navigation/index.js';
|
|
119
|
+
export {
|
|
120
|
+
boolean,
|
|
121
|
+
searchParams,
|
|
122
|
+
string,
|
|
123
|
+
rgb,
|
|
124
|
+
array,
|
|
125
|
+
date,
|
|
126
|
+
json,
|
|
127
|
+
number,
|
|
128
|
+
ParseError,
|
|
129
|
+
ParseSchemaFieldError,
|
|
130
|
+
} from './parsing/index.js';
|
|
119
131
|
export {
|
|
120
132
|
Popup,
|
|
121
133
|
type PopupEventName,
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { mockSessionStorageGetItem } from 'test-utils';
|
|
2
|
+
import type { SpyInstance } from 'vitest';
|
|
3
|
+
import { afterEach, expect, it, vi } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import { createWindow } from '../../../../test-utils/createWindow';
|
|
6
|
+
import { requestViewport } from '../../../viewport/requestViewport';
|
|
7
|
+
import { createViewport } from '../createViewport';
|
|
8
|
+
|
|
9
|
+
vi.mock('../../../viewport/requestViewport', () => {
|
|
10
|
+
return {
|
|
11
|
+
requestViewport: vi.fn(),
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
type RequestViewport = typeof requestViewport;
|
|
16
|
+
|
|
17
|
+
const requestViewportMock = requestViewport as unknown as SpyInstance<Parameters<RequestViewport>, ReturnType<RequestViewport>>;
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
vi.restoreAllMocks();
|
|
21
|
+
requestViewportMock.mockReset();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should create Viewport from the data located in the storage if page was reloaded and storage has viewport data', () => {
|
|
25
|
+
mockSessionStorageGetItem('{"height":992,"isExpanded":false,"stableHeight":992,"width":320}');
|
|
26
|
+
expect(createViewport(true, 'macos', vi.fn(), false)).toMatchObject({
|
|
27
|
+
height: 992,
|
|
28
|
+
isExpanded: false,
|
|
29
|
+
stableHeight: 992,
|
|
30
|
+
width: 320,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should return viewport with window data if page was not reloaded or storage is missing required data, and platform has a stable viewport', () => {
|
|
35
|
+
createWindow({ innerHeight: 1000, innerWidth: 2000 });
|
|
36
|
+
expect(createViewport(false, 'macos', vi.fn(), false)).toMatchObject({
|
|
37
|
+
height: 1000,
|
|
38
|
+
isExpanded: true,
|
|
39
|
+
stableHeight: 1000,
|
|
40
|
+
width: 2000,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
expect(createViewport(true, 'macos', vi.fn(), false)).toMatchObject({
|
|
44
|
+
height: 1000,
|
|
45
|
+
isExpanded: true,
|
|
46
|
+
stableHeight: 1000,
|
|
47
|
+
width: 2000,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should create Viewport instance from the result of calling requestViewport function if page was not reloaded, platform has no stable viewport and initialization is complete', () => {
|
|
52
|
+
requestViewportMock.mockImplementation(async () => ({
|
|
53
|
+
height: 922,
|
|
54
|
+
isStateStable: false,
|
|
55
|
+
width: 800,
|
|
56
|
+
isExpanded: false,
|
|
57
|
+
}));
|
|
58
|
+
|
|
59
|
+
expect(createViewport(false, 'android', vi.fn(), true)).resolves.toMatchObject({
|
|
60
|
+
height: 922,
|
|
61
|
+
isExpanded: false,
|
|
62
|
+
stableHeight: 0,
|
|
63
|
+
width: 800,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
requestViewportMock.mockImplementation(async () => ({
|
|
67
|
+
height: 111,
|
|
68
|
+
isStateStable: true,
|
|
69
|
+
width: 222,
|
|
70
|
+
isExpanded: true,
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
expect(createViewport(false, 'android', vi.fn(), true)).resolves.toMatchObject({
|
|
74
|
+
height: 111,
|
|
75
|
+
isExpanded: true,
|
|
76
|
+
stableHeight: 111,
|
|
77
|
+
width: 222,
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should return viewport with zeroes if page was reloaded, storage doesnt contain data, platform has no stable viewport and initialization is not complete', () => {
|
|
82
|
+
requestViewportMock.mockImplementation(async () => ({
|
|
83
|
+
height: 922,
|
|
84
|
+
isStateStable: false,
|
|
85
|
+
width: 800,
|
|
86
|
+
isExpanded: false,
|
|
87
|
+
}));
|
|
88
|
+
|
|
89
|
+
mockSessionStorageGetItem(null);
|
|
90
|
+
expect(createViewport(true, 'android', vi.fn(), false)).toMatchObject({
|
|
91
|
+
height: 0,
|
|
92
|
+
isExpanded: false,
|
|
93
|
+
stableHeight: 0,
|
|
94
|
+
width: 0,
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { InitData } from '../InitData';
|
|
4
|
+
|
|
5
|
+
it('should return fields specified in constructor', () => {
|
|
6
|
+
const authDate = new Date(123);
|
|
7
|
+
const data1 = new InitData({ authDate, hash: 'hash' });
|
|
8
|
+
expect(data1.authDate).toBe(authDate);
|
|
9
|
+
expect(data1.canSendAfter).toBeUndefined();
|
|
10
|
+
expect(data1.chat).toBeUndefined();
|
|
11
|
+
expect(data1.chatType).toBeUndefined();
|
|
12
|
+
expect(data1.chatInstance).toBeUndefined();
|
|
13
|
+
expect(data1.hash).toBe('hash');
|
|
14
|
+
expect(data1.queryId).toBeUndefined();
|
|
15
|
+
expect(data1.receiver).toBeUndefined();
|
|
16
|
+
expect(data1.startParam).toBeUndefined();
|
|
17
|
+
expect(data1.user).toBeUndefined();
|
|
18
|
+
|
|
19
|
+
const canSendAfter = 1;
|
|
20
|
+
const data2 = new InitData({
|
|
21
|
+
authDate,
|
|
22
|
+
canSendAfter,
|
|
23
|
+
chat: {
|
|
24
|
+
id: 999,
|
|
25
|
+
photoUrl: 'photo',
|
|
26
|
+
type: 'group',
|
|
27
|
+
title: 'Title',
|
|
28
|
+
},
|
|
29
|
+
chatType: 'sender',
|
|
30
|
+
chatInstance: 'abc',
|
|
31
|
+
hash: 'joke',
|
|
32
|
+
queryId: 'query id',
|
|
33
|
+
receiver: {
|
|
34
|
+
id: 1000,
|
|
35
|
+
photoUrl: 'receiver photo',
|
|
36
|
+
firstName: 'a',
|
|
37
|
+
lastName: 'b',
|
|
38
|
+
username: 'c',
|
|
39
|
+
isBot: false,
|
|
40
|
+
isPremium: false,
|
|
41
|
+
languageCode: 'en',
|
|
42
|
+
},
|
|
43
|
+
startParam: 'param',
|
|
44
|
+
user: {
|
|
45
|
+
id: 2000,
|
|
46
|
+
photoUrl: 'user photo',
|
|
47
|
+
firstName: 'a',
|
|
48
|
+
lastName: 'b',
|
|
49
|
+
username: 'c',
|
|
50
|
+
languageCode: 'en',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
expect(data2.authDate).toBe(authDate);
|
|
54
|
+
expect(data2.canSendAfter).toBe(canSendAfter);
|
|
55
|
+
expect(data2.chat).toStrictEqual({
|
|
56
|
+
id: 999,
|
|
57
|
+
photoUrl: 'photo',
|
|
58
|
+
type: 'group',
|
|
59
|
+
title: 'Title',
|
|
60
|
+
});
|
|
61
|
+
expect(data2.chatType).toBe('sender');
|
|
62
|
+
expect(data2.chatInstance).toBe('abc');
|
|
63
|
+
expect(data2.hash).toBe('joke');
|
|
64
|
+
expect(data2.queryId).toBe('query id');
|
|
65
|
+
expect(data2.receiver).toStrictEqual({
|
|
66
|
+
id: 1000,
|
|
67
|
+
photoUrl: 'receiver photo',
|
|
68
|
+
firstName: 'a',
|
|
69
|
+
lastName: 'b',
|
|
70
|
+
username: 'c',
|
|
71
|
+
isBot: false,
|
|
72
|
+
isPremium: false,
|
|
73
|
+
languageCode: 'en',
|
|
74
|
+
});
|
|
75
|
+
expect(data2.startParam).toBe('param');
|
|
76
|
+
expect(data2.user).toStrictEqual({
|
|
77
|
+
id: 2000,
|
|
78
|
+
photoUrl: 'user photo',
|
|
79
|
+
firstName: 'a',
|
|
80
|
+
lastName: 'b',
|
|
81
|
+
username: 'c',
|
|
82
|
+
languageCode: 'en',
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should have canSendAfterData equal to authDate + canSendAfter seconds', () => {
|
|
87
|
+
const authDate = new Date();
|
|
88
|
+
const initData = new InitData({
|
|
89
|
+
authDate,
|
|
90
|
+
hash: 'abc',
|
|
91
|
+
canSendAfter: 32000,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect(initData.canSendAfter).toBe(32000);
|
|
95
|
+
expect(initData.canSendAfterDate).toStrictEqual(
|
|
96
|
+
new Date(authDate.getTime() + 32000000),
|
|
97
|
+
);
|
|
98
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { chatParser } from '../chatParser';
|
|
4
|
+
|
|
5
|
+
describe('id', () => {
|
|
6
|
+
it('should throw an error in case, this property is missing', () => {
|
|
7
|
+
expect(
|
|
8
|
+
() => chatParser().parse({
|
|
9
|
+
type: 'group chat',
|
|
10
|
+
title: 'My chat',
|
|
11
|
+
}),
|
|
12
|
+
).toThrow();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should parse source property as number and pass it to the "id" property', () => {
|
|
16
|
+
expect(
|
|
17
|
+
chatParser().parse({
|
|
18
|
+
id: 882,
|
|
19
|
+
type: 'group chat',
|
|
20
|
+
title: 'My chat',
|
|
21
|
+
}),
|
|
22
|
+
).toMatchObject({
|
|
23
|
+
id: 882,
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('type', () => {
|
|
29
|
+
it('should throw an error in case, this property is missing', () => {
|
|
30
|
+
expect(
|
|
31
|
+
() => chatParser().parse({
|
|
32
|
+
id: 223,
|
|
33
|
+
title: 'My chat',
|
|
34
|
+
}),
|
|
35
|
+
).toThrow();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should parse source property as number and pass it to the "type" property', () => {
|
|
39
|
+
expect(
|
|
40
|
+
chatParser().parse({
|
|
41
|
+
id: 882,
|
|
42
|
+
type: 'group chat',
|
|
43
|
+
title: 'My chat',
|
|
44
|
+
}),
|
|
45
|
+
).toMatchObject({
|
|
46
|
+
type: 'group chat',
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('title', () => {
|
|
52
|
+
it('should throw an error in case, this property is missing', () => {
|
|
53
|
+
expect(
|
|
54
|
+
() => chatParser().parse({
|
|
55
|
+
id: 223,
|
|
56
|
+
type: 'group chat',
|
|
57
|
+
}),
|
|
58
|
+
).toThrow();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should parse source property as number and pass it to the "title" property', () => {
|
|
62
|
+
expect(
|
|
63
|
+
chatParser().parse({
|
|
64
|
+
id: 882,
|
|
65
|
+
type: 'group chat',
|
|
66
|
+
title: 'My chat',
|
|
67
|
+
}),
|
|
68
|
+
).toMatchObject({
|
|
69
|
+
title: 'My chat',
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('photo_url', () => {
|
|
75
|
+
it('should parse source property as number and pass it to the "photoUrl" property', () => {
|
|
76
|
+
expect(
|
|
77
|
+
chatParser().parse({
|
|
78
|
+
id: 882,
|
|
79
|
+
type: 'group chat',
|
|
80
|
+
title: 'My chat',
|
|
81
|
+
photo_url: 'https://image.com',
|
|
82
|
+
}),
|
|
83
|
+
).toMatchObject({
|
|
84
|
+
photoUrl: 'https://image.com',
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('username', () => {
|
|
90
|
+
it('should parse source property as number and pass it to the "username" property', () => {
|
|
91
|
+
expect(
|
|
92
|
+
chatParser().parse({
|
|
93
|
+
id: 882,
|
|
94
|
+
type: 'group chat',
|
|
95
|
+
title: 'My chat',
|
|
96
|
+
username: 'Johny Bravo',
|
|
97
|
+
}),
|
|
98
|
+
).toMatchObject({
|
|
99
|
+
username: 'Johny Bravo',
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|