@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.
Files changed (85) hide show
  1. package/dist/dts/index.d.ts +1 -0
  2. package/dist/dts/launch-params/index.d.ts +1 -0
  3. package/dist/dts/launch-params/retrieveFromUrl.d.ts +6 -0
  4. package/dist/dts/launch-params/types.d.ts +12 -8
  5. package/dist/index.cjs +1 -1
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.iife.js +1 -1
  8. package/dist/index.iife.js.map +1 -1
  9. package/dist/index.mjs +253 -238
  10. package/dist/index.mjs.map +1 -1
  11. package/package.json +2 -2
  12. package/src/__tests__/globals.ts +39 -0
  13. package/src/back-button/__tests__/BackButton.ts +129 -0
  14. package/src/bridge/__tests__/request.ts +236 -0
  15. package/src/bridge/env/__tests__/hasExternalNotify.ts +15 -0
  16. package/src/bridge/env/__tests__/hasWebviewProxy.ts +15 -0
  17. package/src/bridge/env/__tests__/isIframe.ts +30 -0
  18. package/src/bridge/events/__tests__/createEmitter.ts +143 -0
  19. package/src/bridge/events/__tests__/off.ts +34 -0
  20. package/src/bridge/events/__tests__/on.ts +49 -0
  21. package/src/bridge/events/__tests__/onTelegramEvent.ts +51 -0
  22. package/src/bridge/events/__tests__/once.ts +64 -0
  23. package/src/bridge/events/__tests__/singletonEmitter.ts +22 -0
  24. package/src/bridge/events/__tests__/subscribe.ts +49 -0
  25. package/src/bridge/events/__tests__/unsubscribe.ts +34 -0
  26. package/src/bridge/events/parsers/__tests__/clipboardTextReceived.ts +21 -0
  27. package/src/bridge/events/parsers/__tests__/invoiceClosed.ts +12 -0
  28. package/src/bridge/events/parsers/__tests__/popupClosed.ts +10 -0
  29. package/src/bridge/events/parsers/__tests__/qrTextReceived.ts +9 -0
  30. package/src/bridge/events/parsers/__tests__/theme-changed.ts +42 -0
  31. package/src/bridge/events/parsers/__tests__/viewportChanged.ts +49 -0
  32. package/src/bridge/methods/__tests__/createPostEvent.ts +37 -0
  33. package/src/bridge/methods/__tests__/postEvent.ts +137 -0
  34. package/src/classnames/__tests__/classNames.ts +20 -0
  35. package/src/classnames/__tests__/mergeClassNames.ts +21 -0
  36. package/src/closing-behavior/__tests__/ClosingBehavior.ts +86 -0
  37. package/src/colors/__tests__/isColorDark.ts +12 -0
  38. package/src/colors/__tests__/isRGB.ts +13 -0
  39. package/src/colors/__tests__/isRGBShort.ts +13 -0
  40. package/src/colors/__tests__/toRGB.ts +23 -0
  41. package/src/event-emitter/__tests__/EventEmitter.ts +145 -0
  42. package/src/haptic-feedback/__tests__/HapticFeedback.ts +68 -0
  43. package/src/index.ts +12 -0
  44. package/src/init/creators/__tests__/createViewport.ts +96 -0
  45. package/src/init-data/__tests__/InitData.ts +98 -0
  46. package/src/init-data/__tests__/chatParser.ts +102 -0
  47. package/src/init-data/__tests__/initDataParser.ts +136 -0
  48. package/src/init-data/__tests__/parseInitData.ts +136 -0
  49. package/src/init-data/__tests__/userParser.ts +96 -0
  50. package/src/launch-params/__tests__/retrieveFromUrl.ts +19 -0
  51. package/src/launch-params/index.ts +1 -0
  52. package/src/launch-params/launchParamsParser.ts +4 -0
  53. package/src/launch-params/retrieveFromLocation.ts +2 -2
  54. package/src/launch-params/retrieveFromPerformance.ts +2 -7
  55. package/src/launch-params/retrieveFromUrl.ts +19 -0
  56. package/src/launch-params/types.ts +13 -8
  57. package/src/logger/__tests__/Logger.ts +107 -0
  58. package/src/main-button/__tests__/MainButton.ts +346 -0
  59. package/src/mini-app/__tests__/MiniApp.ts +140 -0
  60. package/src/misc/__tests__/isRecord.ts +21 -0
  61. package/src/navigation/HashNavigator/__tests__/HashNavigator.ts +144 -0
  62. package/src/navigation/HashNavigator/__tests__/drop.ts +42 -0
  63. package/src/navigation/HashNavigator/__tests__/go.ts +9 -0
  64. package/src/parsing/__tests__/ArrayValueParser.ts +18 -0
  65. package/src/parsing/__tests__/toRecord.ts +10 -0
  66. package/src/parsing/parsers/__tests__/array.ts +39 -0
  67. package/src/parsing/parsers/__tests__/boolean.ts +31 -0
  68. package/src/parsing/parsers/__tests__/date.ts +25 -0
  69. package/src/parsing/parsers/__tests__/json.ts +80 -0
  70. package/src/parsing/parsers/__tests__/number.ts +23 -0
  71. package/src/parsing/parsers/__tests__/rgb.ts +22 -0
  72. package/src/parsing/parsers/__tests__/searchParams.ts +105 -0
  73. package/src/parsing/parsers/__tests__/string.ts +25 -0
  74. package/src/popup/__tests__/Popup.ts +130 -0
  75. package/src/popup/__tests__/preparePopupParams.ts +85 -0
  76. package/src/supports/__tests__/supports.ts +123 -0
  77. package/src/theme-params/__tests__/keys.ts +19 -0
  78. package/src/theme-params/__tests__/parseThemeParams.ts +29 -0
  79. package/src/theme-params/__tests__/serializeThemeParams.ts +29 -0
  80. package/src/theme-params/__tests__/themeParamsParser.ts +29 -0
  81. package/src/timeout/__tests__/isTimeoutError.ts +9 -0
  82. package/src/timeout/__tests__/withTimeout.ts +28 -0
  83. package/src/version/__tests__/compareVersions.ts +19 -0
  84. package/src/viewport/__tests__/isStableViewportPlatform.ts +15 -0
  85. package/src/viewport/__tests__/utils.ts +12 -0
@@ -0,0 +1,346 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+
3
+ import { MainButton } from '../MainButton';
4
+
5
+ describe('disable', () => {
6
+ it('should call "web_app_setup_main_button" method with parameter where "is_active" property equal to false', () => {
7
+ const postEvent = vi.fn();
8
+ const mainButton = new MainButton({
9
+ text: 'Test',
10
+ textColor: '#000000',
11
+ backgroundColor: '#ffffff',
12
+ postEvent,
13
+ isLoaderVisible: false,
14
+ isVisible: false,
15
+ isEnabled: false,
16
+ });
17
+
18
+ mainButton.disable();
19
+ expect(postEvent).toHaveBeenCalledTimes(1);
20
+ expect(postEvent.mock.calls[0][0]).toBe('web_app_setup_main_button');
21
+ expect(postEvent.mock.calls[0][1]).toHaveProperty('is_active', false);
22
+ });
23
+
24
+ it('should emit "change:isEnabled" event with false value', () => {
25
+ const listener = vi.fn();
26
+ const postEvent = vi.fn();
27
+ const mainButton = new MainButton({
28
+ text: 'Test',
29
+ textColor: '#000000',
30
+ backgroundColor: '#ffffff',
31
+ postEvent,
32
+ isLoaderVisible: false,
33
+ isVisible: false,
34
+ isEnabled: true,
35
+ });
36
+
37
+ mainButton.on('change:isEnabled', listener);
38
+ mainButton.disable();
39
+ expect(listener).toHaveBeenCalledTimes(1);
40
+ expect(listener).toHaveBeenCalledWith(false);
41
+ });
42
+ });
43
+
44
+ describe('enable', () => {
45
+ it('should call "web_app_setup_main_button" method with parameter where "is_active" property equal to true', () => {
46
+ const postEvent = vi.fn();
47
+ const mainButton = new MainButton({
48
+ text: 'Test',
49
+ textColor: '#000000',
50
+ backgroundColor: '#ffffff',
51
+ postEvent,
52
+ isLoaderVisible: false,
53
+ isVisible: false,
54
+ isEnabled: false,
55
+ });
56
+
57
+ mainButton.enable();
58
+ expect(postEvent).toHaveBeenCalledTimes(1);
59
+ expect(postEvent.mock.calls[0][0]).toBe('web_app_setup_main_button');
60
+ expect(postEvent.mock.calls[0][1]).toHaveProperty('is_active', true);
61
+ });
62
+
63
+ it('should emit "change:isEnabled" event with true value', () => {
64
+ const listener = vi.fn();
65
+ const mainButton = new MainButton({
66
+ text: 'Test',
67
+ textColor: '#000000',
68
+ backgroundColor: '#ffffff',
69
+ postEvent: vi.fn(),
70
+ isLoaderVisible: false,
71
+ isVisible: false,
72
+ isEnabled: false,
73
+ });
74
+
75
+ mainButton.on('change:isEnabled', listener);
76
+ mainButton.enable();
77
+ expect(listener).toHaveBeenCalledTimes(1);
78
+ expect(listener).toHaveBeenCalledWith(true);
79
+ });
80
+ });
81
+
82
+ describe('hide', () => {
83
+ it('should call "web_app_setup_main_button" method with parameter where "is_visible" property equal to false', () => {
84
+ const postEvent = vi.fn();
85
+ const mainButton = new MainButton({
86
+ text: 'Test',
87
+ textColor: '#000000',
88
+ backgroundColor: '#ffffff',
89
+ postEvent,
90
+ isLoaderVisible: false,
91
+ isVisible: true,
92
+ isEnabled: false,
93
+ });
94
+
95
+ mainButton.hide();
96
+ expect(postEvent).toHaveBeenCalledTimes(1);
97
+ expect(postEvent.mock.calls[0][0]).toBe('web_app_setup_main_button');
98
+ expect(postEvent.mock.calls[0][1]).toHaveProperty('is_visible', false);
99
+ });
100
+
101
+ it('should emit "change:isVisible" event with false value', () => {
102
+ const listener = vi.fn();
103
+ const mainButton = new MainButton({
104
+ text: 'Test',
105
+ textColor: '#000000',
106
+ backgroundColor: '#ffffff',
107
+ postEvent: vi.fn(),
108
+ isLoaderVisible: false,
109
+ isVisible: true,
110
+ isEnabled: false,
111
+ });
112
+
113
+ mainButton.on('change:isVisible', listener);
114
+ mainButton.hide();
115
+ expect(listener).toHaveBeenCalledTimes(1);
116
+ expect(listener).toHaveBeenCalledWith(false);
117
+ });
118
+ });
119
+
120
+ describe('hideLoader', () => {
121
+ it('should call "web_app_setup_main_button" method with parameter where "is_progress_visible" property equal to false', () => {
122
+ const postEvent = vi.fn();
123
+ const mainButton = new MainButton({
124
+ text: 'Test',
125
+ textColor: '#000000',
126
+ backgroundColor: '#ffffff',
127
+ postEvent,
128
+ isLoaderVisible: true,
129
+ isVisible: false,
130
+ isEnabled: false,
131
+ });
132
+
133
+ mainButton.hideLoader();
134
+ expect(postEvent).toHaveBeenCalledTimes(1);
135
+ expect(postEvent.mock.calls[0][0]).toBe('web_app_setup_main_button');
136
+ expect(postEvent.mock.calls[0][1]).toHaveProperty('is_progress_visible', false);
137
+ });
138
+
139
+ it('should emit "change:isLoaderVisible" event with false value', () => {
140
+ const listener = vi.fn();
141
+ const mainButton = new MainButton({
142
+ text: 'Test',
143
+ textColor: '#000000',
144
+ backgroundColor: '#ffffff',
145
+ postEvent: vi.fn(),
146
+ isLoaderVisible: true,
147
+ isVisible: false,
148
+ isEnabled: false,
149
+ });
150
+
151
+ mainButton.on('change:isLoaderVisible', listener);
152
+ mainButton.hideLoader();
153
+ expect(listener).toHaveBeenCalledTimes(1);
154
+ expect(listener).toHaveBeenCalledWith(false);
155
+ });
156
+ });
157
+
158
+ describe('show', () => {
159
+ it('should call "web_app_setup_main_button" method with parameter where "is_visible" property equal to true', () => {
160
+ const postEvent = vi.fn();
161
+ const mainButton = new MainButton({
162
+ text: 'Test',
163
+ textColor: '#000000',
164
+ backgroundColor: '#ffffff',
165
+ postEvent,
166
+ isLoaderVisible: false,
167
+ isVisible: false,
168
+ isEnabled: false,
169
+ });
170
+
171
+ mainButton.show();
172
+ expect(postEvent).toHaveBeenCalledTimes(1);
173
+ expect(postEvent.mock.calls[0][0]).toBe('web_app_setup_main_button');
174
+ expect(postEvent.mock.calls[0][1]).toHaveProperty('is_visible', true);
175
+ });
176
+
177
+ it('should emit "change:isVisible" event with true value', () => {
178
+ const listener = vi.fn();
179
+ const mainButton = new MainButton({
180
+ text: 'Test',
181
+ textColor: '#000000',
182
+ backgroundColor: '#ffffff',
183
+ postEvent: vi.fn(),
184
+ isLoaderVisible: false,
185
+ isVisible: false,
186
+ isEnabled: false,
187
+ });
188
+
189
+ mainButton.on('change:isVisible', listener);
190
+ mainButton.show();
191
+ expect(listener).toHaveBeenCalledTimes(1);
192
+ expect(listener).toHaveBeenCalledWith(true);
193
+ });
194
+ });
195
+
196
+ describe('showLoader', () => {
197
+ it('should call "web_app_setup_main_button" method with parameter where "is_progress_visible" property equal to true', () => {
198
+ const postEvent = vi.fn();
199
+ const mainButton = new MainButton({
200
+ text: 'Test',
201
+ textColor: '#000000',
202
+ backgroundColor: '#ffffff',
203
+ postEvent,
204
+ isLoaderVisible: false,
205
+ isVisible: false,
206
+ isEnabled: false,
207
+ });
208
+
209
+ mainButton.showLoader();
210
+ expect(postEvent).toHaveBeenCalledTimes(1);
211
+ expect(postEvent.mock.calls[0][0]).toBe('web_app_setup_main_button');
212
+ expect(postEvent.mock.calls[0][1]).toHaveProperty('is_progress_visible', true);
213
+ });
214
+
215
+ it('should emit "change:isLoaderVisible" event with true value', () => {
216
+ const listener = vi.fn();
217
+ const mainButton = new MainButton({
218
+ text: 'Test',
219
+ textColor: '#000000',
220
+ backgroundColor: '#ffffff',
221
+ postEvent: vi.fn(),
222
+ isLoaderVisible: false,
223
+ isVisible: false,
224
+ isEnabled: false,
225
+ });
226
+
227
+ mainButton.on('change:isLoaderVisible', listener);
228
+ mainButton.showLoader();
229
+ expect(listener).toHaveBeenCalledTimes(1);
230
+ expect(listener).toHaveBeenCalledWith(true);
231
+ });
232
+ });
233
+
234
+ describe('setText', () => {
235
+ it('should call "web_app_setup_main_button" method with parameter where "text" property equal to specified value', () => {
236
+ const postEvent = vi.fn();
237
+ const mainButton = new MainButton({
238
+ text: 'Test',
239
+ textColor: '#000000',
240
+ backgroundColor: '#ffffff',
241
+ postEvent,
242
+ isLoaderVisible: false,
243
+ isVisible: false,
244
+ isEnabled: false,
245
+ });
246
+
247
+ mainButton.setText('WOW');
248
+ expect(postEvent).toHaveBeenCalledTimes(1);
249
+ expect(postEvent.mock.calls[0][0]).toBe('web_app_setup_main_button');
250
+ expect(postEvent.mock.calls[0][1]).toHaveProperty('text', 'WOW');
251
+ });
252
+
253
+ it('should emit "change:text" event with specified value', () => {
254
+ const listener = vi.fn();
255
+ const mainButton = new MainButton({
256
+ text: 'Test',
257
+ textColor: '#000000',
258
+ backgroundColor: '#ffffff',
259
+ postEvent: vi.fn(),
260
+ isLoaderVisible: false,
261
+ isVisible: false,
262
+ isEnabled: false,
263
+ });
264
+
265
+ mainButton.on('change:text', listener);
266
+ mainButton.setText('Punch');
267
+ expect(listener).toHaveBeenCalledTimes(1);
268
+ expect(listener).toHaveBeenCalledWith('Punch');
269
+ });
270
+ });
271
+
272
+ describe('setTextColor', () => {
273
+ it('should call "web_app_setup_main_button" method with parameter where "text_color" property equal to specified value', () => {
274
+ const postEvent = vi.fn();
275
+ const mainButton = new MainButton({
276
+ text: 'Test',
277
+ textColor: '#000000',
278
+ backgroundColor: '#ffffff',
279
+ postEvent,
280
+ isLoaderVisible: false,
281
+ isVisible: false,
282
+ isEnabled: false,
283
+ });
284
+
285
+ mainButton.setTextColor('#ffaacc');
286
+ expect(postEvent).toHaveBeenCalledTimes(1);
287
+ expect(postEvent.mock.calls[0][0]).toBe('web_app_setup_main_button');
288
+ expect(postEvent.mock.calls[0][1]).toHaveProperty('text_color', '#ffaacc');
289
+ });
290
+
291
+ it('should emit "change:textColor" event with specified value', () => {
292
+ const listener = vi.fn();
293
+ const mainButton = new MainButton({
294
+ text: 'Test',
295
+ textColor: '#000000',
296
+ backgroundColor: '#ffffff',
297
+ postEvent: vi.fn(),
298
+ isLoaderVisible: false,
299
+ isVisible: false,
300
+ isEnabled: false,
301
+ });
302
+
303
+ mainButton.on('change:textColor', listener);
304
+ mainButton.setTextColor('#aaaaaa');
305
+ expect(listener).toHaveBeenCalledTimes(1);
306
+ expect(listener).toHaveBeenCalledWith('#aaaaaa');
307
+ });
308
+ });
309
+
310
+ describe('setColor', () => {
311
+ it('should call "web_app_setup_main_button" method with parameter where "color" property equal to specified value', () => {
312
+ const postEvent = vi.fn();
313
+ const mainButton = new MainButton({
314
+ text: 'Test',
315
+ textColor: '#000000',
316
+ backgroundColor: '#ffffff',
317
+ postEvent,
318
+ isLoaderVisible: false,
319
+ isVisible: false,
320
+ isEnabled: false,
321
+ });
322
+
323
+ mainButton.setBackgroundColor('#ffaacc');
324
+ expect(postEvent).toHaveBeenCalledTimes(1);
325
+ expect(postEvent.mock.calls[0][0]).toBe('web_app_setup_main_button');
326
+ expect(postEvent.mock.calls[0][1]).toHaveProperty('color', '#ffaacc');
327
+ });
328
+
329
+ it('should emit "change:backgroundColor" event with specified value', () => {
330
+ const listener = vi.fn();
331
+ const mainButton = new MainButton({
332
+ text: 'Test',
333
+ textColor: '#000000',
334
+ backgroundColor: '#ffffff',
335
+ postEvent: vi.fn(),
336
+ isLoaderVisible: false,
337
+ isVisible: false,
338
+ isEnabled: false,
339
+ });
340
+
341
+ mainButton.on('change:backgroundColor', listener);
342
+ mainButton.setBackgroundColor('#aaaaaa');
343
+ expect(listener).toHaveBeenCalledTimes(1);
344
+ expect(listener).toHaveBeenCalledWith('#aaaaaa');
345
+ });
346
+ });
@@ -0,0 +1,140 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+
3
+ import type { PostEvent } from '../../bridge';
4
+ import type { RGB } from '../../colors';
5
+ import { createRequestIdGenerator } from '../../init/creators';
6
+ import { MiniApp } from '../MiniApp';
7
+ import type { MiniAppHeaderColor } from '../types';
8
+
9
+ interface CreateWebAppOptions {
10
+ backgroundColor?: RGB;
11
+ botInline?: boolean;
12
+ headerColor?: MiniAppHeaderColor;
13
+ postEvent?: PostEvent;
14
+ version?: string;
15
+ }
16
+
17
+ function createMiniApp(options: CreateWebAppOptions = {}): MiniApp {
18
+ const {
19
+ postEvent = vi.fn(),
20
+ backgroundColor = '#000000',
21
+ headerColor = 'bg_color',
22
+ version = '6.0',
23
+ botInline = false,
24
+ } = options;
25
+
26
+ return new MiniApp({
27
+ version,
28
+ botInline,
29
+ postEvent,
30
+ headerColor,
31
+ backgroundColor,
32
+ createRequestId: createRequestIdGenerator(),
33
+ });
34
+ }
35
+
36
+ describe('setBackgroundColor', () => {
37
+ it('should call "web_app_set_background_color" method with { color: {{color}} }', () => {
38
+ const postEvent = vi.fn();
39
+ const miniApp = createMiniApp({ postEvent });
40
+
41
+ expect(postEvent).toHaveBeenCalledTimes(0);
42
+ miniApp.setBackgroundColor('#ffaabb');
43
+ expect(postEvent).toHaveBeenCalledTimes(1);
44
+ expect(postEvent).toHaveBeenCalledWith('web_app_set_background_color', { color: '#ffaabb' });
45
+ });
46
+
47
+ it('should emit "change:backgroundColor" event with specified value', () => {
48
+ const miniApp = createMiniApp({ backgroundColor: '#ffffff' });
49
+ const listener = vi.fn();
50
+
51
+ miniApp.on('change:backgroundColor', listener);
52
+ expect(listener).toHaveBeenCalledTimes(0);
53
+ miniApp.setBackgroundColor('#ffaacc');
54
+ expect(listener).toHaveBeenCalledTimes(1);
55
+ expect(listener).toHaveBeenCalledWith('#ffaacc');
56
+ });
57
+ });
58
+
59
+ describe('setHeaderColor', () => {
60
+ it('should call "web_app_set_header_color" method with { color_key: {{color_key}} }', () => {
61
+ const postEvent = vi.fn();
62
+ const miniApp = createMiniApp({ postEvent, headerColor: 'bg_color' });
63
+
64
+ expect(postEvent).toHaveBeenCalledTimes(0);
65
+ miniApp.setHeaderColor('secondary_bg_color');
66
+ expect(postEvent).toHaveBeenCalledTimes(1);
67
+ expect(postEvent).toHaveBeenCalledWith('web_app_set_header_color', { color_key: 'secondary_bg_color' });
68
+ });
69
+
70
+ it('should emit "change:headerColor" event with specified value', () => {
71
+ const miniApp = createMiniApp({ headerColor: 'bg_color' });
72
+ const listener = vi.fn();
73
+
74
+ miniApp.on('change:headerColor', listener);
75
+ expect(listener).toHaveBeenCalledTimes(0);
76
+ miniApp.setHeaderColor('secondary_bg_color');
77
+ expect(listener).toHaveBeenCalledTimes(1);
78
+ expect(listener).toHaveBeenCalledWith('secondary_bg_color');
79
+ });
80
+ });
81
+
82
+ describe('off', () => {
83
+ describe('"change:backgroundColor" event', () => {
84
+ it('should remove event listener from event', () => {
85
+ const listener = vi.fn();
86
+ const miniApp = createMiniApp({ backgroundColor: '#ffffff' });
87
+
88
+ miniApp.on('change:backgroundColor', listener);
89
+
90
+ expect(listener).toHaveBeenCalledTimes(0);
91
+ miniApp.setBackgroundColor('#aaddcc');
92
+ expect(listener).toHaveBeenCalledTimes(1);
93
+
94
+ miniApp.off('change:backgroundColor', listener);
95
+ listener.mockClear();
96
+
97
+ expect(listener).toHaveBeenCalledTimes(0);
98
+ miniApp.setBackgroundColor('#ffaaaa');
99
+ expect(listener).toHaveBeenCalledTimes(0);
100
+ });
101
+ });
102
+
103
+ describe('"change:headerColor" event', () => {
104
+ it('should remove event listener from event', () => {
105
+ const listener = vi.fn();
106
+ const miniApp = createMiniApp({ headerColor: 'bg_color' });
107
+
108
+ miniApp.on('change:headerColor', listener);
109
+
110
+ expect(listener).toHaveBeenCalledTimes(0);
111
+ miniApp.setHeaderColor('secondary_bg_color');
112
+ expect(listener).toHaveBeenCalledTimes(1);
113
+
114
+ miniApp.off('change:headerColor', listener);
115
+ listener.mockClear();
116
+
117
+ expect(listener).toHaveBeenCalledTimes(0);
118
+ miniApp.setHeaderColor('bg_color');
119
+ expect(listener).toHaveBeenCalledTimes(0);
120
+ });
121
+ });
122
+ });
123
+
124
+ describe('supports', () => {
125
+ describe('setHeaderColor / setBackgroundColor', () => {
126
+ it('should return true in case, WebApp version is 6.1 or higher. False, otherwise', () => {
127
+ const miniApp1 = createMiniApp({ version: '6.0' });
128
+ expect(miniApp1.supports('setHeaderColor')).toBe(false);
129
+ expect(miniApp1.supports('setBackgroundColor')).toBe(false);
130
+
131
+ const miniApp2 = createMiniApp({ version: '6.1' });
132
+ expect(miniApp2.supports('setHeaderColor')).toBe(true);
133
+ expect(miniApp2.supports('setBackgroundColor')).toBe(true);
134
+
135
+ const miniApp3 = createMiniApp({ version: '6.2' });
136
+ expect(miniApp3.supports('setHeaderColor')).toBe(true);
137
+ expect(miniApp3.supports('setBackgroundColor')).toBe(true);
138
+ });
139
+ });
140
+ });
@@ -0,0 +1,21 @@
1
+ import { expect, it } from 'vitest';
2
+
3
+ import { isRecord } from '../isRecord';
4
+
5
+ it('should return false for non-object value', () => {
6
+ [true, 123, 'abc'].forEach((v) => {
7
+ expect(isRecord(v)).toBe(false);
8
+ });
9
+ });
10
+
11
+ it('should return false for null value', () => {
12
+ expect(isRecord(null)).toBe(false);
13
+ });
14
+
15
+ it('should return false for array', () => {
16
+ expect(isRecord([])).toBe(false);
17
+ });
18
+
19
+ it('should return true for object', () => {
20
+ expect(isRecord({ a: true })).toBe(true);
21
+ });
@@ -0,0 +1,144 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { HashNavigator } from '../HashNavigator';
4
+
5
+ // TODO: Add more tests.
6
+
7
+ describe('constructor', () => {
8
+ it('should throw an error if entries list is empty', () => {
9
+ expect(() => new HashNavigator([], 0)).toThrow('Entries list should not be empty.');
10
+ });
11
+
12
+ it('should throw an error if cursor equals to higher than entries count', () => {
13
+ expect(() => new HashNavigator([{ pathname: '/' }], 1))
14
+ .toThrow('Cursor should be less than entries count.');
15
+ expect(() => new HashNavigator([{ pathname: '/' }], 2))
16
+ .toThrow('Cursor should be less than entries count.');
17
+ });
18
+ });
19
+
20
+ describe('methods', () => {
21
+ // describe('go', () => {
22
+ // });
23
+ //
24
+ // describe('forward', () => {
25
+ // });
26
+ //
27
+ // describe('back', () => {
28
+ // });
29
+
30
+ describe('getEntries', () => {
31
+ it('should return deep clone of navigator entries', () => {
32
+ const initialEntries = [{
33
+ search: '?b',
34
+ hash: '#c',
35
+ pathname: '/a',
36
+ }];
37
+ const navigator = new HashNavigator(initialEntries, 0);
38
+ const entries = navigator.getEntries();
39
+
40
+ expect(entries).toStrictEqual(initialEntries);
41
+ expect(entries).not.toBe(initialEntries);
42
+
43
+ expect(entries[0]).toStrictEqual(initialEntries[0]);
44
+ expect(entries[0]).not.toBe(initialEntries[0]);
45
+ });
46
+ });
47
+
48
+ // describe('push', () => {
49
+ // });
50
+ //
51
+ describe('replace', () => {
52
+ it('should replace current entry', () => {
53
+ const navigator = new HashNavigator([{
54
+ search: '?b',
55
+ hash: '#c',
56
+ pathname: '/a',
57
+ }], 0);
58
+ const [prevEntry] = navigator.getEntries();
59
+
60
+ expect(navigator.cursor).toBe(0);
61
+ navigator.replace('/b');
62
+ const [entry] = navigator.getEntries();
63
+
64
+ expect(prevEntry).not.toStrictEqual(entry);
65
+ expect(navigator.cursor).toBe(0);
66
+ });
67
+ });
68
+ });
69
+
70
+ describe('getters', () => {
71
+ describe('pathname', () => {
72
+ it('should return current entry pathname', () => {
73
+ expect(new HashNavigator([{ pathname: '/abc' }], 0).pathname).toBe('/abc');
74
+ });
75
+ });
76
+
77
+ describe('search', () => {
78
+ it('should return current entry search', () => {
79
+ expect(
80
+ new HashNavigator([{
81
+ search: '?a=1',
82
+ pathname: '/',
83
+ }], 0).search,
84
+ ).toBe('?a=1');
85
+ });
86
+ });
87
+
88
+ describe('hash', () => {
89
+ it('should return current entry hash', () => {
90
+ expect(
91
+ new HashNavigator([{
92
+ hash: '#abc',
93
+ pathname: '/',
94
+ }], 0).hash,
95
+ ).toBe('#abc');
96
+ });
97
+ });
98
+
99
+ describe('path', () => {
100
+ it('should combine current entry pathname, search and hash', () => {
101
+ expect(
102
+ new HashNavigator([{
103
+ search: '?b',
104
+ hash: '#c',
105
+ pathname: '/a',
106
+ }], 0).path,
107
+ ).toBe('/a?b#c');
108
+ });
109
+ });
110
+
111
+ describe('canGoBack', () => {
112
+ it('should return false if cursor === 0', () => {
113
+ expect(
114
+ new HashNavigator([{ pathname: '/' }], 0).canGoBack,
115
+ ).toBe(false);
116
+ });
117
+
118
+ it('should return true if cursor > 0', () => {
119
+ expect(
120
+ new HashNavigator([
121
+ { pathname: '/a' },
122
+ { pathname: '/b' },
123
+ ], 1).canGoBack,
124
+ ).toBe(true);
125
+ });
126
+ });
127
+
128
+ describe('canGoForward', () => {
129
+ it('should return false if cursor === entries.length - 1', () => {
130
+ expect(
131
+ new HashNavigator([{ pathname: '/' }], 0).canGoForward,
132
+ ).toBe(false);
133
+ });
134
+
135
+ it('should return true if cursor < entries.length - 1', () => {
136
+ expect(
137
+ new HashNavigator([
138
+ { pathname: '/a' },
139
+ { pathname: '/b' },
140
+ ], 0).canGoForward,
141
+ ).toBe(true);
142
+ });
143
+ });
144
+ });
@@ -0,0 +1,42 @@
1
+ import { afterEach, expect, it, vi } from 'vitest';
2
+
3
+ import { drop } from '../drop';
4
+
5
+ // TODO: Add more tests.
6
+
7
+ function mockHistoryLength(length: number) {
8
+ vi
9
+ .spyOn(window.history, 'length', 'get')
10
+ .mockImplementation(() => length);
11
+ }
12
+
13
+ function mockPushState(impl?: History['pushState']) {
14
+ const spy = vi.spyOn(window.history, 'pushState');
15
+
16
+ if (impl) {
17
+ spy.mockImplementation(impl);
18
+ }
19
+ }
20
+
21
+ afterEach(() => {
22
+ vi.restoreAllMocks();
23
+ });
24
+
25
+ it('should do nothing in case, history contains only 1 element', () => {
26
+ const pushStateSpy = vi.fn();
27
+ mockHistoryLength(1);
28
+ mockPushState(pushStateSpy);
29
+
30
+ expect(pushStateSpy).not.toHaveBeenCalled();
31
+ });
32
+
33
+ it('should push empty state', () => {
34
+ const pushStateSpy = vi.fn();
35
+ mockHistoryLength(2);
36
+ mockPushState(pushStateSpy);
37
+
38
+ drop();
39
+
40
+ expect(pushStateSpy).toHaveBeenCalledOnce();
41
+ expect(pushStateSpy).toHaveBeenCalledWith(null, '');
42
+ });
@@ -0,0 +1,9 @@
1
+ import { expect, it } from 'vitest';
2
+
3
+ import { go } from '../go';
4
+
5
+ // TODO: Add more tests.
6
+
7
+ it('should return true if delta is 0', () => {
8
+ expect(go(0)).resolves.toBe(true);
9
+ });