@tma.js/sdk 1.3.0 → 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 (83) hide show
  1. package/dist/dts/launch-params/index.d.ts +1 -0
  2. package/dist/dts/launch-params/retrieveFromUrl.d.ts +6 -0
  3. package/dist/dts/launch-params/types.d.ts +12 -8
  4. package/dist/index.cjs +1 -1
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.iife.js +1 -1
  7. package/dist/index.iife.js.map +1 -1
  8. package/dist/index.mjs +243 -238
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +2 -2
  11. package/src/__tests__/globals.ts +39 -0
  12. package/src/back-button/__tests__/BackButton.ts +129 -0
  13. package/src/bridge/__tests__/request.ts +236 -0
  14. package/src/bridge/env/__tests__/hasExternalNotify.ts +15 -0
  15. package/src/bridge/env/__tests__/hasWebviewProxy.ts +15 -0
  16. package/src/bridge/env/__tests__/isIframe.ts +30 -0
  17. package/src/bridge/events/__tests__/createEmitter.ts +143 -0
  18. package/src/bridge/events/__tests__/off.ts +34 -0
  19. package/src/bridge/events/__tests__/on.ts +49 -0
  20. package/src/bridge/events/__tests__/onTelegramEvent.ts +51 -0
  21. package/src/bridge/events/__tests__/once.ts +64 -0
  22. package/src/bridge/events/__tests__/singletonEmitter.ts +22 -0
  23. package/src/bridge/events/__tests__/subscribe.ts +49 -0
  24. package/src/bridge/events/__tests__/unsubscribe.ts +34 -0
  25. package/src/bridge/events/parsers/__tests__/clipboardTextReceived.ts +21 -0
  26. package/src/bridge/events/parsers/__tests__/invoiceClosed.ts +12 -0
  27. package/src/bridge/events/parsers/__tests__/popupClosed.ts +10 -0
  28. package/src/bridge/events/parsers/__tests__/qrTextReceived.ts +9 -0
  29. package/src/bridge/events/parsers/__tests__/theme-changed.ts +42 -0
  30. package/src/bridge/events/parsers/__tests__/viewportChanged.ts +49 -0
  31. package/src/bridge/methods/__tests__/createPostEvent.ts +37 -0
  32. package/src/bridge/methods/__tests__/postEvent.ts +137 -0
  33. package/src/classnames/__tests__/classNames.ts +20 -0
  34. package/src/classnames/__tests__/mergeClassNames.ts +21 -0
  35. package/src/closing-behavior/__tests__/ClosingBehavior.ts +86 -0
  36. package/src/colors/__tests__/isColorDark.ts +12 -0
  37. package/src/colors/__tests__/isRGB.ts +13 -0
  38. package/src/colors/__tests__/isRGBShort.ts +13 -0
  39. package/src/colors/__tests__/toRGB.ts +23 -0
  40. package/src/event-emitter/__tests__/EventEmitter.ts +145 -0
  41. package/src/haptic-feedback/__tests__/HapticFeedback.ts +68 -0
  42. package/src/init/creators/__tests__/createViewport.ts +96 -0
  43. package/src/init-data/__tests__/InitData.ts +98 -0
  44. package/src/init-data/__tests__/chatParser.ts +102 -0
  45. package/src/init-data/__tests__/initDataParser.ts +136 -0
  46. package/src/init-data/__tests__/parseInitData.ts +136 -0
  47. package/src/init-data/__tests__/userParser.ts +96 -0
  48. package/src/launch-params/__tests__/retrieveFromUrl.ts +19 -0
  49. package/src/launch-params/index.ts +1 -0
  50. package/src/launch-params/launchParamsParser.ts +4 -0
  51. package/src/launch-params/retrieveFromLocation.ts +2 -2
  52. package/src/launch-params/retrieveFromPerformance.ts +2 -7
  53. package/src/launch-params/retrieveFromUrl.ts +19 -0
  54. package/src/launch-params/types.ts +13 -8
  55. package/src/logger/__tests__/Logger.ts +107 -0
  56. package/src/main-button/__tests__/MainButton.ts +346 -0
  57. package/src/mini-app/__tests__/MiniApp.ts +140 -0
  58. package/src/misc/__tests__/isRecord.ts +21 -0
  59. package/src/navigation/HashNavigator/__tests__/HashNavigator.ts +144 -0
  60. package/src/navigation/HashNavigator/__tests__/drop.ts +42 -0
  61. package/src/navigation/HashNavigator/__tests__/go.ts +9 -0
  62. package/src/parsing/__tests__/ArrayValueParser.ts +18 -0
  63. package/src/parsing/__tests__/toRecord.ts +10 -0
  64. package/src/parsing/parsers/__tests__/array.ts +39 -0
  65. package/src/parsing/parsers/__tests__/boolean.ts +31 -0
  66. package/src/parsing/parsers/__tests__/date.ts +25 -0
  67. package/src/parsing/parsers/__tests__/json.ts +80 -0
  68. package/src/parsing/parsers/__tests__/number.ts +23 -0
  69. package/src/parsing/parsers/__tests__/rgb.ts +22 -0
  70. package/src/parsing/parsers/__tests__/searchParams.ts +105 -0
  71. package/src/parsing/parsers/__tests__/string.ts +25 -0
  72. package/src/popup/__tests__/Popup.ts +130 -0
  73. package/src/popup/__tests__/preparePopupParams.ts +85 -0
  74. package/src/supports/__tests__/supports.ts +123 -0
  75. package/src/theme-params/__tests__/keys.ts +19 -0
  76. package/src/theme-params/__tests__/parseThemeParams.ts +29 -0
  77. package/src/theme-params/__tests__/serializeThemeParams.ts +29 -0
  78. package/src/theme-params/__tests__/themeParamsParser.ts +29 -0
  79. package/src/timeout/__tests__/isTimeoutError.ts +9 -0
  80. package/src/timeout/__tests__/withTimeout.ts +28 -0
  81. package/src/version/__tests__/compareVersions.ts +19 -0
  82. package/src/viewport/__tests__/isStableViewportPlatform.ts +15 -0
  83. package/src/viewport/__tests__/utils.ts +12 -0
@@ -0,0 +1,49 @@
1
+ import { afterEach, beforeEach, expect, it, vi } from 'vitest';
2
+
3
+ import { createWindow, type WindowSpy } from '../../../../test-utils/createWindow';
4
+ import { dispatchWindowMessageEvent } from '../../../../test-utils/dispatchWindowMessageEvent';
5
+ import { on } from '../on';
6
+
7
+ let windowSpy: WindowSpy;
8
+
9
+ beforeEach(() => {
10
+ windowSpy = createWindow();
11
+ });
12
+
13
+ afterEach(() => {
14
+ windowSpy.mockRestore();
15
+ });
16
+
17
+ it('should call listener in case, Telegram event was created', () => {
18
+ const listener = vi.fn();
19
+ on('viewport_changed', listener);
20
+
21
+ const eventData = {
22
+ height: 123,
23
+ width: 321,
24
+ is_expanded: false,
25
+ is_state_stable: false,
26
+ };
27
+ dispatchWindowMessageEvent('viewport_changed', eventData);
28
+
29
+ expect(listener).toHaveBeenCalledTimes(1);
30
+ expect(listener).toHaveBeenCalledWith(eventData);
31
+ });
32
+
33
+ it('should remove listener in case, returned callback was called', () => {
34
+ const listener = vi.fn();
35
+ const emit = () => dispatchWindowMessageEvent('viewport_changed', {
36
+ height: 123,
37
+ width: 321,
38
+ is_expanded: false,
39
+ is_state_stable: false,
40
+ });
41
+
42
+ const off = on('viewport_changed', listener);
43
+ emit();
44
+ expect(listener).toHaveBeenCalledTimes(1);
45
+
46
+ off();
47
+ emit();
48
+ expect(listener).toHaveBeenCalledTimes(1);
49
+ });
@@ -0,0 +1,51 @@
1
+ import { afterEach, beforeEach, expect, it, vi } from 'vitest';
2
+
3
+ import { createWindow, type WindowSpy } from '../../../../test-utils/createWindow';
4
+ import { dispatchWindowMessageEvent } from '../../../../test-utils/dispatchWindowMessageEvent';
5
+ import { onTelegramEvent } from '../onTelegramEvent';
6
+
7
+ let windowSpy: WindowSpy;
8
+
9
+ beforeEach(() => {
10
+ windowSpy = createWindow();
11
+ });
12
+
13
+ afterEach(() => {
14
+ windowSpy.mockRestore();
15
+ });
16
+
17
+ it('should call passed callback with event type and data in case, window generated "message" event with data, presented as object with properties "eventType" (string) and "eventData" (unknown). Object is converted to string.', () => {
18
+ const callback = vi.fn();
19
+ onTelegramEvent(callback);
20
+
21
+ dispatchWindowMessageEvent('qr_text_received', {});
22
+
23
+ expect(callback).toHaveBeenCalledTimes(1);
24
+ expect(callback).toHaveBeenCalledWith('qr_text_received', {});
25
+ });
26
+
27
+ it('should not define event handlers twice in case, window object contains "TelegramGameProxy_receiveEvent" property.', () => {
28
+ (window as any).TelegramGameProxy_receiveEvent = true;
29
+
30
+ onTelegramEvent(vi.fn());
31
+ expect(window).not.toHaveProperty('Telegram');
32
+ });
33
+
34
+ it('should call passed callback with event type and data in case, external environment generated event.', () => {
35
+ const callback = vi.fn();
36
+ onTelegramEvent(callback);
37
+
38
+ (window as any).TelegramGameProxy_receiveEvent('test', false);
39
+
40
+ expect(callback).toHaveBeenCalledTimes(1);
41
+ expect(callback).toHaveBeenCalledWith('test', false);
42
+ });
43
+
44
+ it('should ignore a message event with unexpected data', () => {
45
+ const callback = vi.fn();
46
+ onTelegramEvent(callback);
47
+
48
+ window.dispatchEvent(new MessageEvent('message', { data: null }));
49
+
50
+ expect(callback).toHaveBeenCalledTimes(0);
51
+ });
@@ -0,0 +1,64 @@
1
+ import { afterEach, beforeEach, expect, it, vi } from 'vitest';
2
+
3
+ import { createWindow, type WindowSpy } from '../../../../test-utils/createWindow';
4
+ import { dispatchWindowMessageEvent } from '../../../../test-utils/dispatchWindowMessageEvent';
5
+ import { once } from '../once';
6
+
7
+ let windowSpy: WindowSpy;
8
+
9
+ beforeEach(() => {
10
+ windowSpy = createWindow();
11
+ });
12
+
13
+ afterEach(() => {
14
+ windowSpy.mockRestore();
15
+ });
16
+
17
+ it('should call listener in case, Telegram event was created', () => {
18
+ const listener = vi.fn();
19
+ once('viewport_changed', listener);
20
+
21
+ const eventData = {
22
+ height: 123,
23
+ width: 321,
24
+ is_expanded: false,
25
+ is_state_stable: false,
26
+ };
27
+ dispatchWindowMessageEvent('viewport_changed', eventData);
28
+
29
+ expect(listener).toHaveBeenCalledTimes(1);
30
+ expect(listener).toHaveBeenCalledWith(eventData);
31
+ });
32
+
33
+ it('should remove listener in case, returned callback was called', () => {
34
+ const listener = vi.fn();
35
+ const emit = () => dispatchWindowMessageEvent('viewport_changed', {
36
+ height: 123,
37
+ width: 321,
38
+ is_expanded: false,
39
+ is_state_stable: false,
40
+ });
41
+
42
+ const off = once('viewport_changed', listener);
43
+
44
+ off();
45
+ emit();
46
+ expect(listener).toHaveBeenCalledTimes(0);
47
+ });
48
+
49
+ it('should remove listener in case, listener was called', () => {
50
+ const listener = vi.fn();
51
+ const emit = () => dispatchWindowMessageEvent('viewport_changed', {
52
+ height: 123,
53
+ width: 321,
54
+ is_expanded: false,
55
+ is_state_stable: false,
56
+ });
57
+
58
+ once('viewport_changed', listener);
59
+ emit();
60
+ expect(listener).toHaveBeenCalledTimes(1);
61
+
62
+ emit();
63
+ expect(listener).toHaveBeenCalledTimes(1);
64
+ });
@@ -0,0 +1,22 @@
1
+ import { afterEach, beforeEach, expect, it } from 'vitest';
2
+
3
+ import type { WindowSpy } from '../../../../test-utils/createWindow';
4
+ import { createWindow } from '../../../../test-utils/createWindow';
5
+ import { singletonEmitter } from '../singletonEmitter';
6
+
7
+ let windowSpy: WindowSpy;
8
+
9
+ beforeEach(() => {
10
+ windowSpy = createWindow({
11
+ innerWidth: 1920,
12
+ innerHeight: 1080,
13
+ });
14
+ });
15
+
16
+ afterEach(() => {
17
+ windowSpy.mockRestore();
18
+ });
19
+
20
+ it('should return the same instance of emitter', () => {
21
+ expect(singletonEmitter()).toEqual(singletonEmitter());
22
+ });
@@ -0,0 +1,49 @@
1
+ import { afterEach, beforeEach, expect, it, vi } from 'vitest';
2
+
3
+ import { createWindow, type WindowSpy } from '../../../../test-utils/createWindow';
4
+ import { dispatchWindowMessageEvent } from '../../../../test-utils/dispatchWindowMessageEvent';
5
+ import { subscribe } from '../subscribe';
6
+
7
+ let windowSpy: WindowSpy;
8
+
9
+ beforeEach(() => {
10
+ windowSpy = createWindow();
11
+ });
12
+
13
+ afterEach(() => {
14
+ windowSpy.mockRestore();
15
+ });
16
+
17
+ it('should call listener in case, Telegram event was created', () => {
18
+ const listener = vi.fn();
19
+ subscribe(listener);
20
+
21
+ const eventData = {
22
+ height: 123,
23
+ width: 321,
24
+ is_expanded: false,
25
+ is_state_stable: false,
26
+ };
27
+ dispatchWindowMessageEvent('viewport_changed', eventData);
28
+
29
+ expect(listener).toHaveBeenCalledTimes(1);
30
+ expect(listener).toHaveBeenCalledWith('viewport_changed', eventData);
31
+ });
32
+
33
+ it('should remove listener in case, returned callback was called', () => {
34
+ const listener = vi.fn();
35
+ const emit = () => dispatchWindowMessageEvent('viewport_changed', {
36
+ height: 123,
37
+ width: 321,
38
+ is_expanded: false,
39
+ is_state_stable: false,
40
+ });
41
+
42
+ const unsubscribe = subscribe(listener);
43
+ emit();
44
+ expect(listener).toHaveBeenCalledTimes(1);
45
+
46
+ unsubscribe();
47
+ emit();
48
+ expect(listener).toHaveBeenCalledTimes(1);
49
+ });
@@ -0,0 +1,34 @@
1
+ import { afterEach, beforeEach, expect, it, vi } from 'vitest';
2
+
3
+ import { createWindow, type WindowSpy } from '../../../../test-utils/createWindow';
4
+ import { dispatchWindowMessageEvent } from '../../../../test-utils/dispatchWindowMessageEvent';
5
+ import { subscribe } from '../subscribe';
6
+ import { unsubscribe } from '../unsubscribe';
7
+
8
+ let windowSpy: WindowSpy;
9
+
10
+ beforeEach(() => {
11
+ windowSpy = createWindow();
12
+ });
13
+
14
+ afterEach(() => {
15
+ windowSpy.mockRestore();
16
+ });
17
+
18
+ it('should remove listener', () => {
19
+ const listener = vi.fn();
20
+ const emit = () => dispatchWindowMessageEvent('viewport_changed', {
21
+ height: 123,
22
+ width: 321,
23
+ is_expanded: false,
24
+ is_state_stable: false,
25
+ });
26
+
27
+ subscribe(listener);
28
+ emit();
29
+ expect(listener).toHaveBeenCalledTimes(1);
30
+
31
+ unsubscribe(listener);
32
+ emit();
33
+ expect(listener).toHaveBeenCalledTimes(1);
34
+ });
@@ -0,0 +1,21 @@
1
+ import { expect, it } from 'vitest';
2
+
3
+ import { clipboardTextReceived } from '../clipboardTextReceived';
4
+
5
+ it('should return parsed value in case, passed value satisfies schema', () => {
6
+ const cases = [
7
+ { req_id: 'abc', data: 'ok' },
8
+ { req_id: 'abc' },
9
+ { req_id: 'abc', data: null },
10
+ ];
11
+
12
+ cases.forEach((value) => {
13
+ expect(clipboardTextReceived().parse(value)).toStrictEqual(value);
14
+ expect(clipboardTextReceived().parse(JSON.stringify(value)))
15
+ .toStrictEqual(value);
16
+ });
17
+ });
18
+
19
+ it('should throw an error in case, passed value does not satisfy schema', () => {
20
+ expect(() => clipboardTextReceived().parse({})).toThrow();
21
+ });
@@ -0,0 +1,12 @@
1
+ import { expect, it } from 'vitest';
2
+
3
+ import { invoiceClosed } from '../invoiceClosed';
4
+
5
+ it('should return parsed value in case, passed value satisfies schema', () => {
6
+ const value = { slug: 'abc', status: 'def' };
7
+ expect(invoiceClosed().parse(value)).toStrictEqual(value);
8
+ });
9
+
10
+ it('should throw an error in case, passed value does not satisfy schema', () => {
11
+ expect(() => invoiceClosed().parse({})).toThrow();
12
+ });
@@ -0,0 +1,10 @@
1
+ import { expect, it } from 'vitest';
2
+
3
+ import { popupClosed } from '../popupClosed';
4
+
5
+ it('should return parsed value in case, passed value satisfies schema', () => {
6
+ expect(popupClosed().parse({ button_id: 'ok' })).toStrictEqual({ button_id: 'ok' });
7
+ expect(popupClosed().parse({})).toStrictEqual({});
8
+ expect(popupClosed().parse({ button_id: null })).toStrictEqual({});
9
+ expect(popupClosed().parse({ button_id: 100 })).toStrictEqual({ button_id: '100' });
10
+ });
@@ -0,0 +1,9 @@
1
+ import { expect, it } from 'vitest';
2
+
3
+ import { qrTextReceived } from '../qrTextReceived';
4
+
5
+ it('should return parsed value in case, passed value satisfies schema', () => {
6
+ expect(qrTextReceived().parse({ data: 'ok' })).toStrictEqual({ data: 'ok' });
7
+ expect(qrTextReceived().parse({})).toStrictEqual({});
8
+ expect(qrTextReceived().parse({ data: 100 })).toStrictEqual({ data: '100' });
9
+ });
@@ -0,0 +1,42 @@
1
+ import { expect, it } from 'vitest';
2
+
3
+ import { themeChanged } from '../theme-changed';
4
+
5
+ it('should return parsed value in case, passed value satisfies schema', () => {
6
+ const values = [
7
+ {
8
+ theme_params: {
9
+ accent_text_color: '#aaccbb',
10
+ bg_color: '#ffaabb',
11
+ button_color: '#faaafa',
12
+ button_text_color: '#666271',
13
+ destructive_text_color: '#111332',
14
+ header_bg_color: '#aab133',
15
+ hint_color: '#113322',
16
+ link_color: '#882133',
17
+ secondary_bg_color: '#2231aa',
18
+ section_bg_color: '#111332',
19
+ section_header_text_color: '#111332',
20
+ subtitle_text_color: '#111332',
21
+ text_color: '#bbaadd',
22
+ },
23
+ },
24
+ {
25
+ theme_params: {},
26
+ },
27
+ ];
28
+
29
+ values.forEach((value) => {
30
+ expect(themeChanged().parse(value)).toStrictEqual(value);
31
+ expect(themeChanged().parse(JSON.stringify(value))).toStrictEqual(value);
32
+ });
33
+ });
34
+
35
+ it('should throw an error in case, passed value does not satisfy schema', () => {
36
+ expect(() => themeChanged().parse({})).toThrow();
37
+ expect(() => themeChanged().parse({
38
+ theme_params: {
39
+ bg_color: 'Hello there!',
40
+ },
41
+ })).toThrow();
42
+ });
@@ -0,0 +1,49 @@
1
+ import { afterEach, beforeEach, expect, it, vi } from 'vitest';
2
+
3
+ import { viewportChanged } from '../viewportChanged';
4
+
5
+ const windowSpy = vi.spyOn(window, 'window', 'get');
6
+ // const innerWidth = 2000;
7
+
8
+ beforeEach(() => {
9
+ windowSpy.mockImplementation(() => ({ innerWidth: 2000 }) as any);
10
+ });
11
+
12
+ afterEach(() => {
13
+ windowSpy.mockRestore();
14
+ });
15
+
16
+ it('should return parsed value in case, passed value satisfies schema', () => {
17
+ const values = [
18
+ {
19
+ height: 900,
20
+ is_state_stable: true,
21
+ is_expanded: true,
22
+ },
23
+ {
24
+ width: null,
25
+ height: 900,
26
+ is_state_stable: true,
27
+ is_expanded: true,
28
+ },
29
+ {
30
+ height: 900,
31
+ width: 100,
32
+ is_state_stable: true,
33
+ is_expanded: true,
34
+ },
35
+ ];
36
+
37
+ values.forEach((value) => {
38
+ const width = !value.width ? window.innerWidth : value.width;
39
+ expect(viewportChanged().parse(value)).toStrictEqual({ ...value, width });
40
+ expect(viewportChanged().parse(JSON.stringify(value))).toStrictEqual({
41
+ ...value,
42
+ width,
43
+ });
44
+ });
45
+ });
46
+
47
+ it('should throw an error in case, passed value does not satisfy schema', () => {
48
+ expect(() => viewportChanged().parse({})).toThrow();
49
+ });
@@ -0,0 +1,37 @@
1
+ import { expect, it, vi } from 'vitest';
2
+
3
+ import { createPostEvent } from '../../index';
4
+ import * as postEventModule from '../postEvent';
5
+
6
+ vi.mock('~/bridge/methods/postEvent.js', () => ({
7
+ postEvent: vi.fn(),
8
+ }));
9
+
10
+ it('should throw error if passed method is unsupported in specified version', () => {
11
+ const postEvent = createPostEvent('6.0');
12
+ expect(() => postEvent('web_app_request_write_access'))
13
+ .toThrow('Method "web_app_request_write_access" is unsupported in the Mini Apps version 6.0.');
14
+ });
15
+
16
+ it('should throw error if passed method parameter is unsupported in specified version', () => {
17
+ const postEvent = createPostEvent('6.3');
18
+ expect(() => postEvent('web_app_open_link', {
19
+ url: '',
20
+ try_instant_view: true,
21
+ }))
22
+ .toThrow('Parameter "try_instant_view" in method "web_app_open_link" is unsupported in the Mini Apps version 6.3.');
23
+
24
+ expect(() => postEvent('web_app_set_header_color', {
25
+ color: '#aaaaaa',
26
+ }))
27
+ .toThrow('Parameter "color" in method "web_app_set_header_color" is unsupported in the Mini Apps version 6.3.');
28
+ });
29
+
30
+ it('should call global postEvent function', () => {
31
+ const postEvent = createPostEvent('6.3');
32
+ const spy = vi.spyOn(postEventModule, 'postEvent');
33
+
34
+ postEvent('web_app_request_viewport');
35
+ expect(spy).toHaveBeenCalledOnce();
36
+ expect(spy).toHaveBeenCalledWith('web_app_request_viewport', undefined);
37
+ });
@@ -0,0 +1,137 @@
1
+ import {
2
+ afterEach,
3
+ beforeAll,
4
+ expect,
5
+ it,
6
+ type SpyInstance,
7
+ vi,
8
+ } from 'vitest';
9
+
10
+ import { setTargetOrigin } from '../../../globals';
11
+ import { postEvent } from '../../index';
12
+
13
+ let windowSpy: SpyInstance<[], Window & typeof globalThis>;
14
+
15
+ beforeAll(() => {
16
+ windowSpy = vi.spyOn(window, 'window', 'get');
17
+ });
18
+
19
+ afterEach(() => {
20
+ windowSpy.mockReset();
21
+ });
22
+
23
+ it('should call "window.parent.postMessage" with object with properties {eventType: string, eventData: any} converted to string in case, current environment is iframe', () => {
24
+ const postMessageSpy = vi.fn();
25
+ windowSpy.mockImplementation(() => ({
26
+ self: 1000,
27
+ top: 900,
28
+ parent: { postMessage: postMessageSpy },
29
+ }) as any);
30
+
31
+ expect(postMessageSpy).toHaveBeenCalledTimes(0);
32
+ postEvent('web_app_close');
33
+ expect(postMessageSpy).toHaveBeenCalledTimes(1);
34
+ expect(postMessageSpy).toHaveBeenCalledWith(JSON.stringify({
35
+ eventType: 'web_app_close',
36
+ eventData: undefined,
37
+ }), 'https://web.telegram.org');
38
+
39
+ postMessageSpy.mockClear();
40
+
41
+ expect(postMessageSpy).toHaveBeenCalledTimes(0);
42
+ postEvent('web_app_set_header_color', { color_key: 'bg_color' });
43
+ expect(postMessageSpy).toHaveBeenCalledTimes(1);
44
+ expect(postMessageSpy).toHaveBeenCalledWith(JSON.stringify({
45
+ eventType: 'web_app_set_header_color',
46
+ eventData: { color_key: 'bg_color' },
47
+ }), 'https://web.telegram.org');
48
+
49
+ postMessageSpy.mockClear();
50
+
51
+ expect(postMessageSpy).toHaveBeenCalledTimes(0);
52
+ postEvent('web_app_close', { targetOrigin: 'abc' });
53
+ expect(postMessageSpy).toHaveBeenCalledTimes(1);
54
+ expect(postMessageSpy).toHaveBeenCalledWith(JSON.stringify({
55
+ eventType: 'web_app_close',
56
+ eventData: undefined,
57
+ }), 'abc');
58
+
59
+ postMessageSpy.mockClear();
60
+
61
+ expect(postMessageSpy).toHaveBeenCalledTimes(0);
62
+ postEvent(
63
+ 'web_app_set_header_color',
64
+ { color_key: 'bg_color' },
65
+ { targetOrigin: 'abc' },
66
+ );
67
+ expect(postMessageSpy).toHaveBeenCalledTimes(1);
68
+ expect(postMessageSpy).toHaveBeenCalledWith(JSON.stringify({
69
+ eventType: 'web_app_set_header_color',
70
+ eventData: { color_key: 'bg_color' },
71
+ }), 'abc');
72
+ });
73
+
74
+ it('should call "window.TelegramWebviewProxy.postEvent" in case this path exists. Function accepts event name (string) as the first argument and event data (object converted to string) as the second one.', () => {
75
+ const spy = vi.fn();
76
+ windowSpy.mockImplementation(() => ({
77
+ TelegramWebviewProxy: { postEvent: spy },
78
+ }) as any);
79
+
80
+ // Without parameters.
81
+ expect(spy).toHaveBeenCalledTimes(0);
82
+ postEvent('web_app_close');
83
+ expect(spy).toHaveBeenCalledTimes(1);
84
+ expect(spy).toHaveBeenCalledWith('web_app_close', undefined);
85
+
86
+ spy.mockClear();
87
+
88
+ // With parameters.
89
+ expect(spy).toHaveBeenCalledTimes(0);
90
+ postEvent('web_app_set_header_color', { color_key: 'bg_color' });
91
+ expect(spy).toHaveBeenCalledTimes(1);
92
+ expect(spy).toHaveBeenCalledWith('web_app_set_header_color', '{"color_key":"bg_color"}');
93
+ });
94
+
95
+ it('should call "window.external.notify" in case it exists. Passed value is object, converted to string. This object should contain fields "eventType" and "eventData".', () => {
96
+ const spy = vi.fn();
97
+ windowSpy.mockImplementation(() => ({
98
+ external: { notify: spy },
99
+ }) as any);
100
+
101
+ // Without parameters.
102
+ expect(spy).toHaveBeenCalledTimes(0);
103
+ postEvent('web_app_close');
104
+ expect(spy).toHaveBeenCalledTimes(1);
105
+ expect(spy).toHaveBeenCalledWith('{"eventType":"web_app_close"}');
106
+
107
+ spy.mockClear();
108
+
109
+ // With parameters.
110
+ expect(spy).toHaveBeenCalledTimes(0);
111
+ postEvent('web_app_set_header_color', { color_key: 'bg_color' });
112
+ expect(spy).toHaveBeenCalledTimes(1);
113
+ expect(spy).toHaveBeenCalledWith('{"eventType":"web_app_set_header_color","eventData":{"color_key":"bg_color"}}');
114
+ });
115
+
116
+ it('should throw an error in case, current environment is unknown', () => {
117
+ windowSpy.mockImplementation(() => ({}) as any);
118
+ expect(() => postEvent('web_app_close'))
119
+ .toThrow('Unable to determine current environment and possible way to send event');
120
+ });
121
+
122
+ it('should use globally set target origin', () => {
123
+ const postMessageSpy = vi.fn();
124
+ windowSpy.mockImplementation(() => ({
125
+ self: 1000,
126
+ top: 900,
127
+ parent: { postMessage: postMessageSpy },
128
+ }) as any);
129
+
130
+ setTargetOrigin('here we go!');
131
+ postEvent('iframe_ready');
132
+
133
+ expect(postMessageSpy).toHaveBeenCalledWith(
134
+ JSON.stringify({ eventType: 'iframe_ready' }),
135
+ 'here we go!',
136
+ );
137
+ });
@@ -0,0 +1,20 @@
1
+ import { expect, it } from 'vitest';
2
+
3
+ import { classNames } from '../classNames';
4
+
5
+ it('should ignore all non-empty strings and objects', () => {
6
+ expect(classNames('', 2, 'b', null, undefined, false, true, [], 'a', 'c')).toBe('b a c');
7
+ });
8
+
9
+ it('should pick only keys which values are truthy', () => {
10
+ expect(classNames({
11
+ a: true,
12
+ b: null,
13
+ c: false,
14
+ d: undefined,
15
+ e: {},
16
+ f: 3,
17
+ g: 0,
18
+ h: '',
19
+ })).toBe('a e f');
20
+ });
@@ -0,0 +1,21 @@
1
+ import { expect, it } from 'vitest';
2
+
3
+ import { mergeClassNames } from '../mergeClassNames';
4
+
5
+ it('should ignore non-object values', () => {
6
+ expect(mergeClassNames({}, null, undefined, false, true, {}));
7
+ });
8
+
9
+ it('should merge objects keys by values applying classNames function', () => {
10
+ expect(mergeClassNames(
11
+ { a: 'hey there', b: 'space' },
12
+ { a: 'John', b: 'station' },
13
+ { c: 'wowow' },
14
+ { f: { mod: true, ignore: false, add: 'non empty string' } },
15
+ )).toStrictEqual({
16
+ a: 'hey there John',
17
+ b: 'space station',
18
+ c: 'wowow',
19
+ f: 'mod add',
20
+ });
21
+ });