@tma.js/sdk 1.2.1 → 1.4.0

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 (97) hide show
  1. package/dist/dts/index.d.ts +1 -1
  2. package/dist/dts/init/creators/createViewport.d.ts +2 -9
  3. package/dist/dts/init/init.d.ts +2 -0
  4. package/dist/dts/init/types.d.ts +7 -4
  5. package/dist/dts/launch-params/index.d.ts +1 -0
  6. package/dist/dts/launch-params/retrieveFromUrl.d.ts +6 -0
  7. package/dist/dts/launch-params/types.d.ts +12 -8
  8. package/dist/dts/types/platform.d.ts +1 -1
  9. package/dist/dts/viewport/index.d.ts +1 -0
  10. package/dist/dts/viewport/isStableViewportPlatform.d.ts +7 -0
  11. package/dist/index.cjs +1 -1
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.iife.js +1 -1
  14. package/dist/index.iife.js.map +1 -1
  15. package/dist/index.mjs +469 -467
  16. package/dist/index.mjs.map +1 -1
  17. package/package.json +2 -2
  18. package/src/__tests__/globals.ts +39 -0
  19. package/src/back-button/__tests__/BackButton.ts +129 -0
  20. package/src/bridge/__tests__/request.ts +236 -0
  21. package/src/bridge/env/__tests__/hasExternalNotify.ts +15 -0
  22. package/src/bridge/env/__tests__/hasWebviewProxy.ts +15 -0
  23. package/src/bridge/env/__tests__/isIframe.ts +30 -0
  24. package/src/bridge/events/__tests__/createEmitter.ts +143 -0
  25. package/src/bridge/events/__tests__/off.ts +34 -0
  26. package/src/bridge/events/__tests__/on.ts +49 -0
  27. package/src/bridge/events/__tests__/onTelegramEvent.ts +51 -0
  28. package/src/bridge/events/__tests__/once.ts +64 -0
  29. package/src/bridge/events/__tests__/singletonEmitter.ts +22 -0
  30. package/src/bridge/events/__tests__/subscribe.ts +49 -0
  31. package/src/bridge/events/__tests__/unsubscribe.ts +34 -0
  32. package/src/bridge/events/parsers/__tests__/clipboardTextReceived.ts +21 -0
  33. package/src/bridge/events/parsers/__tests__/invoiceClosed.ts +12 -0
  34. package/src/bridge/events/parsers/__tests__/popupClosed.ts +10 -0
  35. package/src/bridge/events/parsers/__tests__/qrTextReceived.ts +9 -0
  36. package/src/bridge/events/parsers/__tests__/theme-changed.ts +42 -0
  37. package/src/bridge/events/parsers/__tests__/viewportChanged.ts +49 -0
  38. package/src/bridge/methods/__tests__/createPostEvent.ts +37 -0
  39. package/src/bridge/methods/__tests__/postEvent.ts +137 -0
  40. package/src/classnames/__tests__/classNames.ts +20 -0
  41. package/src/classnames/__tests__/mergeClassNames.ts +21 -0
  42. package/src/closing-behavior/__tests__/ClosingBehavior.ts +86 -0
  43. package/src/colors/__tests__/isColorDark.ts +12 -0
  44. package/src/colors/__tests__/isRGB.ts +13 -0
  45. package/src/colors/__tests__/isRGBShort.ts +13 -0
  46. package/src/colors/__tests__/toRGB.ts +23 -0
  47. package/src/event-emitter/__tests__/EventEmitter.ts +145 -0
  48. package/src/haptic-feedback/__tests__/HapticFeedback.ts +68 -0
  49. package/src/index.ts +1 -0
  50. package/src/init/creators/__tests__/createViewport.ts +96 -0
  51. package/src/init/creators/createViewport.ts +60 -81
  52. package/src/init/init.ts +13 -15
  53. package/src/init/types.ts +8 -4
  54. package/src/init-data/__tests__/InitData.ts +98 -0
  55. package/src/init-data/__tests__/chatParser.ts +102 -0
  56. package/src/init-data/__tests__/initDataParser.ts +136 -0
  57. package/src/init-data/__tests__/parseInitData.ts +136 -0
  58. package/src/init-data/__tests__/userParser.ts +96 -0
  59. package/src/launch-params/__tests__/retrieveFromUrl.ts +19 -0
  60. package/src/launch-params/index.ts +1 -0
  61. package/src/launch-params/launchParamsParser.ts +4 -0
  62. package/src/launch-params/retrieveFromLocation.ts +2 -2
  63. package/src/launch-params/retrieveFromPerformance.ts +2 -7
  64. package/src/launch-params/retrieveFromUrl.ts +19 -0
  65. package/src/launch-params/types.ts +13 -8
  66. package/src/logger/__tests__/Logger.ts +107 -0
  67. package/src/main-button/__tests__/MainButton.ts +346 -0
  68. package/src/mini-app/__tests__/MiniApp.ts +140 -0
  69. package/src/misc/__tests__/isRecord.ts +21 -0
  70. package/src/navigation/HashNavigator/__tests__/HashNavigator.ts +144 -0
  71. package/src/navigation/HashNavigator/__tests__/drop.ts +42 -0
  72. package/src/navigation/HashNavigator/__tests__/go.ts +9 -0
  73. package/src/parsing/__tests__/ArrayValueParser.ts +18 -0
  74. package/src/parsing/__tests__/toRecord.ts +10 -0
  75. package/src/parsing/parsers/__tests__/array.ts +39 -0
  76. package/src/parsing/parsers/__tests__/boolean.ts +31 -0
  77. package/src/parsing/parsers/__tests__/date.ts +25 -0
  78. package/src/parsing/parsers/__tests__/json.ts +80 -0
  79. package/src/parsing/parsers/__tests__/number.ts +23 -0
  80. package/src/parsing/parsers/__tests__/rgb.ts +22 -0
  81. package/src/parsing/parsers/__tests__/searchParams.ts +105 -0
  82. package/src/parsing/parsers/__tests__/string.ts +25 -0
  83. package/src/popup/__tests__/Popup.ts +130 -0
  84. package/src/popup/__tests__/preparePopupParams.ts +85 -0
  85. package/src/supports/__tests__/supports.ts +123 -0
  86. package/src/theme-params/__tests__/keys.ts +19 -0
  87. package/src/theme-params/__tests__/parseThemeParams.ts +29 -0
  88. package/src/theme-params/__tests__/serializeThemeParams.ts +29 -0
  89. package/src/theme-params/__tests__/themeParamsParser.ts +29 -0
  90. package/src/timeout/__tests__/isTimeoutError.ts +9 -0
  91. package/src/timeout/__tests__/withTimeout.ts +28 -0
  92. package/src/types/platform.ts +2 -2
  93. package/src/version/__tests__/compareVersions.ts +19 -0
  94. package/src/viewport/__tests__/isStableViewportPlatform.ts +15 -0
  95. package/src/viewport/__tests__/utils.ts +12 -0
  96. package/src/viewport/index.ts +1 -0
  97. package/src/viewport/isStableViewportPlatform.ts +10 -0
@@ -1,4 +1,4 @@
1
- import { parseLaunchParams } from './parseLaunchParams.js';
1
+ import { retrieveFromUrl } from './retrieveFromUrl.js';
2
2
  import type { LaunchParams } from './types.js';
3
3
 
4
4
  /**
@@ -6,5 +6,5 @@ import type { LaunchParams } from './types.js';
6
6
  * @throws {Error} window.location.hash contains invalid data.
7
7
  */
8
8
  export function retrieveFromLocation(): LaunchParams {
9
- return parseLaunchParams(window.location.hash.slice(1));
9
+ return retrieveFromUrl(window.location.href);
10
10
  }
@@ -1,5 +1,5 @@
1
1
  import { getFirstNavigationEntry } from './getFirstNavigationEntry.js';
2
- import { parseLaunchParams } from './parseLaunchParams.js';
2
+ import { retrieveFromUrl } from './retrieveFromUrl.js';
3
3
  import type { LaunchParams } from './types.js';
4
4
 
5
5
  /**
@@ -14,10 +14,5 @@ export function retrieveFromPerformance(): LaunchParams {
14
14
  throw new Error('Unable to get first navigation entry.');
15
15
  }
16
16
 
17
- const hashMatch = navigationEntry.name.match(/#(.*)/);
18
- if (!hashMatch) {
19
- throw new Error('First navigation entry does not contain hash part.');
20
- }
21
-
22
- return parseLaunchParams(hashMatch[1]);
17
+ return retrieveFromUrl(navigationEntry.name);
23
18
  }
@@ -0,0 +1,19 @@
1
+ import { parseLaunchParams } from '~/launch-params/parseLaunchParams.js';
2
+ import type { LaunchParams } from '~/launch-params/types.js';
3
+
4
+ /**
5
+ * Retrieves launch parameters from the specified URL.
6
+ * @param url - URL to extract launch parameters from.
7
+ */
8
+ export function retrieveFromUrl(url: string): LaunchParams {
9
+ const queryParams = url.includes('?')
10
+ // If URL includes "?", we expect it to possibly contain "#". The reason is TMA allows passing
11
+ // start parameter via tgWebAppStartParam **query** parameter. Replacing "#" with "&" we
12
+ // expect merging query parameters with hash parameters and creating complete launch
13
+ // parameters information.
14
+ ? url.replace('#', '&').slice(url.indexOf('?') + 1)
15
+ // Otherwise, we expect specifying launch parameters only in the hash part.
16
+ : url.slice(url.indexOf('#') + 1);
17
+
18
+ return parseLaunchParams(queryParams);
19
+ }
@@ -8,9 +8,9 @@ import type { Platform } from '~/types/index.js';
8
8
  */
9
9
  export interface LaunchParams {
10
10
  /**
11
- * Current Mini Apps version.
11
+ * True if Mini App is currently launched in inline mode.
12
12
  */
13
- version: string;
13
+ botInline?: boolean;
14
14
 
15
15
  /**
16
16
  * Current launch init data. Can be missing in case, application was launched via
@@ -29,19 +29,24 @@ export interface LaunchParams {
29
29
  platform: Platform;
30
30
 
31
31
  /**
32
- * Mini App palette settings.
32
+ * True if application is required to show the Settings Button.
33
33
  */
34
- themeParams: ThemeParamsParsed;
34
+ showSettings?: boolean;
35
35
 
36
36
  /**
37
- * True if Mini App is currently launched in inline mode.
37
+ * Start parameter passed in the application link.
38
38
  */
39
- botInline?: boolean;
39
+ startParam?: string;
40
40
 
41
41
  /**
42
- * True if application is required to show the Settings Button.
42
+ * Mini App palette settings.
43
43
  */
44
- showSettings?: boolean;
44
+ themeParams: ThemeParamsParsed;
45
+
46
+ /**
47
+ * Current Mini Apps version.
48
+ */
49
+ version: string;
45
50
  }
46
51
 
47
52
  export interface LaunchData {
@@ -0,0 +1,107 @@
1
+ import {
2
+ afterAll,
3
+ afterEach,
4
+ beforeAll,
5
+ describe,
6
+ expect,
7
+ it,
8
+ vi,
9
+ } from 'vitest';
10
+
11
+ import { Logger } from '../Logger';
12
+
13
+ const now = new Date('2022-11-04T09:09:43.007Z');
14
+
15
+ afterEach(() => {
16
+ vi.resetAllMocks();
17
+ });
18
+
19
+ beforeAll(() => {
20
+ vi.useFakeTimers().setSystemTime(now);
21
+ });
22
+
23
+ afterAll(() => {
24
+ vi.useRealTimers();
25
+ });
26
+
27
+ it('should not call console methods if logger is disabled', () => {
28
+ const logger = new Logger('', false);
29
+ const spy = vi.fn();
30
+
31
+ vi.spyOn(console, 'log').mockImplementation(spy);
32
+ vi.spyOn(console, 'warn').mockImplementation(spy);
33
+ vi.spyOn(console, 'error').mockImplementation(spy);
34
+
35
+ logger.log();
36
+ logger.warn();
37
+ logger.error();
38
+ expect(spy).not.toHaveBeenCalled();
39
+ });
40
+
41
+ describe('enable', () => {
42
+ it('should start logging messages into console', () => {
43
+ const logger = new Logger('', false);
44
+ const spy = vi.fn();
45
+
46
+ vi.spyOn(console, 'log').mockImplementation(spy);
47
+ logger.log();
48
+ expect(spy).not.toHaveBeenCalled();
49
+
50
+ logger.enable();
51
+ logger.log();
52
+ expect(spy).toHaveBeenCalledOnce();
53
+ });
54
+ });
55
+
56
+ describe('disable', () => {
57
+ it('should stop logging messages into console', () => {
58
+ const logger = new Logger('', true);
59
+ const spy = vi.fn();
60
+
61
+ vi.spyOn(console, 'log').mockImplementation(spy);
62
+ logger.log();
63
+ expect(spy).toHaveBeenCalledOnce();
64
+
65
+ spy.mockReset();
66
+
67
+ logger.disable();
68
+ logger.log();
69
+ expect(spy).not.toHaveBeenCalled();
70
+ });
71
+ });
72
+
73
+ describe('error', () => {
74
+ it('should call console.error()', () => {
75
+ const logger = new Logger('My prefix', true);
76
+ const spy = vi.fn();
77
+ vi.spyOn(console, 'error').mockImplementation(spy);
78
+
79
+ logger.error('Test');
80
+ expect(spy).toHaveBeenCalledOnce();
81
+ expect(spy).toHaveBeenCalledWith('[09:09:43.007]', 'My prefix', 'Test');
82
+ });
83
+ });
84
+
85
+ describe('log', () => {
86
+ it('should call console.log()', () => {
87
+ const logger = new Logger('My prefix', true);
88
+ const spy = vi.fn();
89
+ vi.spyOn(console, 'log').mockImplementation(spy);
90
+
91
+ logger.log('Test');
92
+ expect(spy).toHaveBeenCalledOnce();
93
+ expect(spy).toHaveBeenCalledWith('[09:09:43.007]', 'My prefix', 'Test');
94
+ });
95
+ });
96
+
97
+ describe('warn', () => {
98
+ it('should call console.warn()', () => {
99
+ const logger = new Logger('My prefix', true);
100
+ const spy = vi.fn();
101
+ vi.spyOn(console, 'warn').mockImplementation(spy);
102
+
103
+ logger.warn('Test');
104
+ expect(spy).toHaveBeenCalledOnce();
105
+ expect(spy).toHaveBeenCalledWith('[09:09:43.007]', 'My prefix', 'Test');
106
+ });
107
+ });
@@ -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
+ });