@notabene/javascript-sdk 2.7.0 → 2.7.1-next.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.
@@ -383,7 +383,7 @@ export declare function decodeFragmentToObject(fragment: string): Record<string,
383
383
  * An object representing a deposit transaction
384
384
  * @public
385
385
  */
386
- export declare interface Deposit extends OriginatorFields, DepositTransaction {
386
+ export declare interface Deposit extends OriginatorFields, DepositTransaction, Refreshable {
387
387
  }
388
388
 
389
389
  /**
@@ -1262,6 +1262,15 @@ export declare type Ready = {
1262
1262
  type: CMType.READY;
1263
1263
  };
1264
1264
 
1265
+ declare interface Refreshable {
1266
+ refreshSource?: RefreshSource;
1267
+ }
1268
+
1269
+ declare interface RefreshSource {
1270
+ url: URI;
1271
+ key: string;
1272
+ }
1273
+
1265
1274
  declare type RequestID = UUID;
1266
1275
 
1267
1276
  /**
@@ -1628,7 +1637,7 @@ export declare interface Wallet extends Agent {
1628
1637
  * An object representing a withdrawal transaction
1629
1638
  * @public
1630
1639
  */
1631
- export declare interface Withdrawal extends BeneficiaryFields, Transaction {
1640
+ export declare interface Withdrawal extends BeneficiaryFields, Transaction, Refreshable {
1632
1641
  }
1633
1642
 
1634
1643
  export { }
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "author": "Notabene <developers@notabene.id>",
11
11
  "license": "MIT",
12
12
  "packageManager": "yarn@4.5.1",
13
- "version": "2.7.0",
13
+ "version": "2.7.1-next.1",
14
14
  "source": "src/notabene.ts",
15
15
  "main": "dist/notabene.js",
16
16
  "types": "dist/notabene.d.ts",
@@ -0,0 +1,222 @@
1
+ import { beforeEach, describe, expect, test, vi } from 'vitest';
2
+ import EmbeddedComponent from '../components/EmbeddedComponent';
3
+ import { CMType, HMType } from '../types';
4
+
5
+ // Simple test interfaces
6
+ interface TestValue {
7
+ name?: string;
8
+ amount?: number;
9
+ }
10
+
11
+ interface TestOptions {
12
+ currency?: string;
13
+ }
14
+
15
+ // Mock MessageEventManager
16
+ vi.mock('../utils/MessageEventManager', () => {
17
+ return {
18
+ MessageEventManager: vi.fn().mockImplementation(() => ({
19
+ on: vi.fn((type, callback) => {
20
+ callbacks.set(type, callback);
21
+ return () => callbacks.delete(type);
22
+ }),
23
+ send: vi.fn((message) => {
24
+ const callback = callbacks.get(message.type);
25
+ if (callback) callback(message);
26
+ }),
27
+ off: vi.fn(),
28
+ setPort: vi.fn(),
29
+ })),
30
+ };
31
+ });
32
+
33
+ // Store callbacks for testing
34
+ const callbacks = new Map();
35
+
36
+ describe('EmbeddedComponent', () => {
37
+ let mockParent: HTMLElement;
38
+ let mockAppendChild: ReturnType<typeof vi.fn>;
39
+
40
+ beforeEach(() => {
41
+ // Clear callbacks before each test
42
+ callbacks.clear();
43
+
44
+ mockParent = document.createElement('div');
45
+ vi.spyOn(document, 'querySelector').mockReturnValue(mockParent);
46
+ mockAppendChild = vi.fn().mockImplementation((node: Node) => node);
47
+ vi.spyOn(mockParent, 'appendChild').mockImplementation(mockAppendChild);
48
+ });
49
+
50
+ describe('initialization', () => {
51
+ test('should initialize with basic values', () => {
52
+ const url = 'https://example.com';
53
+ const value = { name: 'Test' };
54
+ const options = { currency: 'USD' };
55
+
56
+ const component = new EmbeddedComponent<TestValue, TestOptions>(
57
+ url,
58
+ value,
59
+ options,
60
+ );
61
+
62
+ expect(component.url).toBe(url);
63
+ expect(component.value).toEqual(value);
64
+ expect(component.options).toEqual(options);
65
+ expect(component.errors).toEqual([]);
66
+ });
67
+
68
+ test('should initialize without options', () => {
69
+ const url = 'https://example.com';
70
+ const value = { name: 'Test' };
71
+
72
+ const component = new EmbeddedComponent<TestValue, TestOptions>(
73
+ url,
74
+ value,
75
+ );
76
+
77
+ expect(component.url).toBe(url);
78
+ expect(component.value).toEqual(value);
79
+ expect(component.options).toBeUndefined();
80
+ expect(component.errors).toEqual([]);
81
+ });
82
+ });
83
+
84
+ describe('value updates', () => {
85
+ test('should update value', () => {
86
+ const component = new EmbeddedComponent<TestValue, TestOptions>(
87
+ 'https://example.com',
88
+ {},
89
+ );
90
+ const newValue = { name: 'Updated', amount: 100 };
91
+
92
+ component.update(newValue);
93
+
94
+ expect(component.value).toEqual(newValue);
95
+ });
96
+
97
+ test('should update value and options', () => {
98
+ const component = new EmbeddedComponent<TestValue, TestOptions>(
99
+ 'https://example.com',
100
+ {},
101
+ );
102
+ const newValue = { name: 'Updated', amount: 100 };
103
+ const newOptions = { currency: 'EUR' };
104
+
105
+ component.update(newValue, newOptions);
106
+
107
+ expect(component.value).toEqual(newValue);
108
+ expect(component.options).toEqual(newOptions);
109
+ });
110
+ });
111
+
112
+ describe('mounting', () => {
113
+ test('should mount component to parent element', () => {
114
+ const component = new EmbeddedComponent<TestValue, TestOptions>(
115
+ 'https://example.com',
116
+ {},
117
+ );
118
+
119
+ component.mount('#parent');
120
+
121
+ expect(document.querySelector).toHaveBeenCalledWith('#parent');
122
+ const iframe = mockAppendChild.mock.calls[0][0] as HTMLIFrameElement;
123
+ expect(iframe).toBeTruthy();
124
+ expect(iframe.src).toBe('https://example.com&embedded=true/');
125
+ expect(iframe.style.width).toBe('100%');
126
+ expect(iframe.style.height).toBe('0px');
127
+ expect(iframe.style.border).toBe('');
128
+ });
129
+
130
+ test('should throw error if parent element not found', () => {
131
+ vi.spyOn(document, 'querySelector').mockReturnValue(null);
132
+ const component = new EmbeddedComponent<TestValue, TestOptions>(
133
+ 'https://example.com',
134
+ {},
135
+ );
136
+
137
+ expect(() => component.mount('#nonexistent')).toThrow(
138
+ 'parentID #nonexistent not found',
139
+ );
140
+ });
141
+
142
+ test('should remove existing iframe when mounting again', () => {
143
+ const component = new EmbeddedComponent<TestValue, TestOptions>(
144
+ 'https://example.com',
145
+ {},
146
+ );
147
+
148
+ // First mount
149
+ component.mount('#parent');
150
+ const firstIframe = mockAppendChild.mock.calls[0][0] as HTMLIFrameElement;
151
+ const removeSpy = vi.spyOn(firstIframe, 'remove');
152
+
153
+ // Second mount
154
+ component.mount('#parent');
155
+
156
+ expect(removeSpy).toHaveBeenCalled();
157
+ expect(mockAppendChild).toHaveBeenCalledTimes(2);
158
+ });
159
+ });
160
+
161
+ describe('event handling', () => {
162
+ test('should register and unregister event listeners', () => {
163
+ const component = new EmbeddedComponent<TestValue, TestOptions>(
164
+ 'https://example.com',
165
+ {},
166
+ );
167
+ const mockCallback = vi.fn();
168
+
169
+ // Register event listener
170
+ const unsubscribe = component.on(HMType.UPDATE, mockCallback);
171
+
172
+ // Send a message
173
+ component.send({ type: HMType.UPDATE, value: { name: 'test' } });
174
+ expect(mockCallback).toHaveBeenCalledTimes(1);
175
+ expect(mockCallback).toHaveBeenCalledWith({
176
+ type: HMType.UPDATE,
177
+ value: { name: 'test' },
178
+ });
179
+
180
+ // Unsubscribe and send again
181
+ unsubscribe();
182
+ component.send({ type: HMType.UPDATE, value: { name: 'test2' } });
183
+ expect(mockCallback).toHaveBeenCalledTimes(1); // Still 1, not called again
184
+ });
185
+
186
+ test('should handle resize events', () => {
187
+ const component = new EmbeddedComponent<TestValue, TestOptions>(
188
+ 'https://example.com',
189
+ {},
190
+ );
191
+ component.mount('#parent');
192
+
193
+ const iframe = mockAppendChild.mock.calls[0][0] as HTMLIFrameElement;
194
+
195
+ // Get the resize callback and trigger it directly
196
+ const resizeCallback = callbacks.get(CMType.RESIZE);
197
+ resizeCallback({ type: CMType.RESIZE, height: 500 });
198
+
199
+ expect(iframe.style.height).toBe('500px');
200
+ });
201
+
202
+ test('should handle validation errors', () => {
203
+ const component = new EmbeddedComponent<TestValue, TestOptions>(
204
+ 'https://example.com',
205
+ {},
206
+ );
207
+ const mockErrors = [{ field: 'name', message: 'Required' }];
208
+ const mockValue = { name: '' };
209
+
210
+ // Get the validation callback and trigger it directly
211
+ const validationCallback = callbacks.get(CMType.INVALID);
212
+ validationCallback({
213
+ type: CMType.INVALID,
214
+ errors: mockErrors,
215
+ value: mockValue,
216
+ });
217
+
218
+ expect(component.errors).toEqual(mockErrors);
219
+ expect(component.value).toEqual(mockValue);
220
+ });
221
+ });
222
+ });
package/src/types.ts CHANGED
@@ -531,6 +531,15 @@ export interface Transaction extends ComponentRequest {
531
531
  };
532
532
  }
533
533
 
534
+ export interface RefreshSource {
535
+ url: URI;
536
+ key: string;
537
+ }
538
+
539
+ export interface Refreshable {
540
+ refreshSource?: RefreshSource;
541
+ }
542
+
534
543
  /**
535
544
  * Extended transaction interface for deposits
536
545
  * @public
@@ -543,13 +552,19 @@ export interface DepositTransaction extends Transaction {
543
552
  * An object representing a withdrawal transaction
544
553
  * @public
545
554
  */
546
- export interface Withdrawal extends BeneficiaryFields, Transaction {}
555
+ export interface Withdrawal
556
+ extends BeneficiaryFields,
557
+ Transaction,
558
+ Refreshable {}
547
559
 
548
560
  /**
549
561
  * An object representing a deposit transaction
550
562
  * @public
551
563
  */
552
- export interface Deposit extends OriginatorFields, DepositTransaction {}
564
+ export interface Deposit
565
+ extends OriginatorFields,
566
+ DepositTransaction,
567
+ Refreshable {}
553
568
 
554
569
  /**
555
570
  * An object representing a request for a deposit