@sundaeswap/sprinkles 0.1.1 → 0.2.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.
@@ -1,23 +1,31 @@
1
1
  import { describe, test, expect, mock, beforeEach } from "bun:test";
2
2
  import { Type } from "@sinclair/typebox";
3
3
 
4
- // Track select calls
4
+ // Track calls and responses
5
5
  let selectCalls = [];
6
6
  let selectResponses = [];
7
+ let confirmResponses = [];
8
+ let inputResponses = [];
7
9
 
8
10
  // Mock @inquirer/prompts
9
11
  mock.module("@inquirer/prompts", () => ({
10
- input: mock(async () => "test"),
12
+ input: mock(async () => {
13
+ const response = inputResponses.shift();
14
+ return response ?? "";
15
+ }),
11
16
  password: mock(async () => "secret"),
12
17
  select: mock(async opts => {
13
18
  selectCalls.push(opts);
14
19
  const response = selectResponses.shift();
15
20
  if (response === undefined) {
16
- // Default: return -1 (Back/Exit)
17
- return -1;
21
+ return "cancel";
18
22
  }
19
23
  return response;
20
24
  }),
25
+ confirm: mock(async () => {
26
+ const response = confirmResponses.shift();
27
+ return response ?? false;
28
+ }),
21
29
  search: mock(async () => "result")
22
30
  }));
23
31
 
@@ -38,232 +46,465 @@ const {
38
46
  const TestSchema = Type.Object({
39
47
  name: Type.String()
40
48
  });
41
- describe("TxDialog (2.6)", () => {
49
+
50
+ // Helper to create a mock transaction
51
+ const createMockTx = (cbor = "deadbeef") => ({
52
+ toCbor: () => cbor,
53
+ body: () => ({
54
+ toCbor: () => cbor,
55
+ requiredSigners: () => null,
56
+ certs: () => null,
57
+ withdrawals: () => null
58
+ }),
59
+ witnessSet: () => ({
60
+ vkeys: () => null,
61
+ setVkeys: () => {}
62
+ }),
63
+ setWitnessSet: () => {}
64
+ });
65
+ describe("TxDialog", () => {
42
66
  beforeEach(() => {
43
67
  selectCalls = [];
44
68
  selectResponses = [];
69
+ confirmResponses = [];
70
+ inputResponses = [];
45
71
  clipboardContent = "";
46
72
  clipboardShouldFail = false;
47
73
  });
48
- test("shows truncated CBOR by default", async () => {
49
- const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog");
50
- const logs = [];
51
- const origLog = console.log;
52
- console.log = (...args) => logs.push(args.join(" "));
53
-
54
- // Just exit immediately
55
- selectResponses = [-1];
56
- const mockTx = {
57
- toCbor: () => "a".repeat(100),
58
- body: () => ({
59
- requiredSigners: () => null,
60
- certs: () => null,
61
- withdrawals: () => null
62
- })
63
- };
64
- const mockBlaze = {
65
- wallet: {
66
- getUsedAddresses: async () => []
67
- }
68
- };
69
- await sprinkle.TxDialog(mockBlaze, mockTx);
70
- console.log = origLog;
71
-
72
- // Should show truncated CBOR (50 chars + ...)
73
- const cborLog = logs.find(l => l.includes("Transaction CBOR:"));
74
- expect(cborLog).toBeDefined();
75
- expect(cborLog.length).toBeLessThan(100);
76
- expect(cborLog).toContain("...");
74
+ describe("Display", () => {
75
+ test("shows truncated CBOR by default", async () => {
76
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog");
77
+ const logs = [];
78
+ const origLog = console.log;
79
+ console.log = (...args) => logs.push(args.join(" "));
80
+ selectResponses = ["cancel"];
81
+ const mockTx = createMockTx("a".repeat(100));
82
+ const mockBlaze = {
83
+ wallet: {
84
+ getUsedAddresses: async () => []
85
+ }
86
+ };
87
+ await sprinkle.TxDialog(mockBlaze, mockTx);
88
+ console.log = origLog;
89
+ const cborLog = logs.find(l => l.includes("Transaction CBOR:"));
90
+ expect(cborLog).toBeDefined();
91
+ expect(cborLog.length).toBeLessThan(100);
92
+ expect(cborLog).toContain("...");
93
+ });
94
+ test("shows transaction hash", async () => {
95
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog-hash");
96
+ const logs = [];
97
+ const origLog = console.log;
98
+ console.log = (...args) => logs.push(args.join(" "));
99
+ selectResponses = ["cancel"];
100
+ const mockTx = createMockTx();
101
+ const mockBlaze = {
102
+ wallet: {
103
+ getUsedAddresses: async () => []
104
+ }
105
+ };
106
+ await sprinkle.TxDialog(mockBlaze, mockTx);
107
+ console.log = origLog;
108
+ const txLog = logs.find(l => l.includes("Transaction:"));
109
+ expect(txLog).toBeDefined();
110
+ });
111
+ test("shows signature count", async () => {
112
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog-sigs");
113
+ const logs = [];
114
+ const origLog = console.log;
115
+ console.log = (...args) => logs.push(args.join(" "));
116
+ selectResponses = ["cancel"];
117
+ const mockTx = createMockTx();
118
+ const mockBlaze = {
119
+ wallet: {
120
+ getUsedAddresses: async () => []
121
+ }
122
+ };
123
+ await sprinkle.TxDialog(mockBlaze, mockTx);
124
+ console.log = origLog;
125
+ const sigLog = logs.find(l => l.includes("Signatures:"));
126
+ expect(sigLog).toBeDefined();
127
+ expect(sigLog).toContain("0");
128
+ });
77
129
  });
78
- test("menu items for non-HotWallet include Expand, Copy, no Sign", async () => {
79
- const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog2");
80
- const origLog = console.log;
81
- console.log = () => {};
82
- selectResponses = [-1];
83
- const mockTx = {
84
- toCbor: () => "deadbeef",
85
- body: () => ({
86
- requiredSigners: () => null,
87
- certs: () => null,
88
- withdrawals: () => null
89
- })
90
- };
91
- const mockBlaze = {
92
- wallet: {
93
- getUsedAddresses: async () => []
94
- }
95
- };
96
- await sprinkle.TxDialog(mockBlaze, mockTx);
97
- console.log = origLog;
98
-
99
- // Check the select call had Expand and Copy but not Sign
100
- const menuCall = selectCalls[0];
101
- expect(menuCall).toBeDefined();
102
- const choiceNames = menuCall.choices.map(c => c.name);
103
- expect(choiceNames).toContain("Expand CBOR");
104
- expect(choiceNames).toContain("Copy CBOR to clipboard");
105
- expect(choiceNames).not.toContain("Sign and submit transaction");
130
+ describe("Menu options", () => {
131
+ test("non-HotWallet has Expand, Copy, Import, Submit, Cancel - no Sign", async () => {
132
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog2");
133
+ const origLog = console.log;
134
+ console.log = () => {};
135
+ selectResponses = ["cancel"];
136
+ const mockTx = createMockTx();
137
+ const mockBlaze = {
138
+ wallet: {
139
+ getUsedAddresses: async () => []
140
+ }
141
+ };
142
+ await sprinkle.TxDialog(mockBlaze, mockTx);
143
+ console.log = origLog;
144
+ const menuCall = selectCalls[0];
145
+ expect(menuCall).toBeDefined();
146
+ const choiceNames = menuCall.choices.map(c => c.name);
147
+ expect(choiceNames).toContain("Expand CBOR");
148
+ expect(choiceNames).toContain("Copy CBOR to clipboard");
149
+ expect(choiceNames).toContain("Import signatures from CBOR");
150
+ expect(choiceNames).toContain("Submit transaction");
151
+ expect(choiceNames).toContain("Cancel");
152
+ expect(choiceNames).not.toContain("Sign with this wallet");
153
+ });
154
+ test("HotWallet has Sign option", async () => {
155
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog3");
156
+ const origLog = console.log;
157
+ console.log = () => {};
158
+ const {
159
+ HotWallet
160
+ } = await import("@blaze-cardano/sdk");
161
+ selectResponses = ["cancel"];
162
+ const mockTx = createMockTx();
163
+ const hotWalletProto = HotWallet.prototype;
164
+ const mockWallet = Object.create(hotWalletProto);
165
+ mockWallet.getUsedAddresses = async () => [];
166
+ const mockBlaze = {
167
+ wallet: mockWallet
168
+ };
169
+ await sprinkle.TxDialog(mockBlaze, mockTx);
170
+ console.log = origLog;
171
+ const menuCall = selectCalls[0];
172
+ expect(menuCall).toBeDefined();
173
+ const choiceNames = menuCall.choices.map(c => c.name);
174
+ expect(choiceNames).toContain("Sign with this wallet");
175
+ });
176
+ });
177
+ describe("Copy CBOR", () => {
178
+ test("copies CBOR to clipboard", async () => {
179
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog4");
180
+ const origLog = console.log;
181
+ const logs = [];
182
+ console.log = (...args) => logs.push(args.join(" "));
183
+ selectResponses = ["copy", "cancel"];
184
+ const txCbor = "deadbeefcafebabe";
185
+ const mockTx = createMockTx(txCbor);
186
+ const mockBlaze = {
187
+ wallet: {
188
+ getUsedAddresses: async () => []
189
+ }
190
+ };
191
+ await sprinkle.TxDialog(mockBlaze, mockTx);
192
+ console.log = origLog;
193
+ expect(clipboardContent).toBe(txCbor);
194
+ expect(logs.some(l => l.includes("copied to clipboard"))).toBe(true);
195
+ });
196
+ test("clipboard failure falls back to expanded view", async () => {
197
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog5");
198
+ const origLog = console.log;
199
+ const logs = [];
200
+ console.log = (...args) => logs.push(args.join(" "));
201
+ clipboardShouldFail = true;
202
+ selectResponses = ["copy", "cancel"];
203
+ const txCbor = "a".repeat(100);
204
+ const mockTx = createMockTx(txCbor);
205
+ const mockBlaze = {
206
+ wallet: {
207
+ getUsedAddresses: async () => []
208
+ }
209
+ };
210
+ await sprinkle.TxDialog(mockBlaze, mockTx);
211
+ console.log = origLog;
212
+ expect(logs.some(l => l.includes("Failed to copy"))).toBe(true);
213
+ });
106
214
  });
107
- test("menu items for HotWallet include Sign", async () => {
108
- const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog3");
109
- const origLog = console.log;
110
- console.log = () => {};
215
+ describe("Expand CBOR", () => {
216
+ test("shows full content when expanded", async () => {
217
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog6");
218
+ const origLog = console.log;
219
+ const logs = [];
220
+ console.log = (...args) => logs.push(args.join(" "));
221
+ selectResponses = ["expand", "cancel"];
222
+ const txCbor = "b".repeat(100);
223
+ const mockTx = createMockTx(txCbor);
224
+ const mockBlaze = {
225
+ wallet: {
226
+ getUsedAddresses: async () => []
227
+ }
228
+ };
229
+ await sprinkle.TxDialog(mockBlaze, mockTx);
230
+ console.log = origLog;
111
231
 
112
- // Need to import HotWallet to use instanceof
113
- const {
114
- HotWallet
115
- } = await import("@blaze-cardano/sdk");
116
- selectResponses = [-1];
117
- const mockTx = {
118
- toCbor: () => "deadbeef",
119
- body: () => ({
120
- requiredSigners: () => null,
121
- certs: () => null,
122
- withdrawals: () => null
123
- })
124
- };
232
+ // First shows truncated, second shows full
233
+ const truncatedLog = logs.find(l => l.includes("...") && l.includes("Transaction CBOR:"));
234
+ const fullLog = logs.find(l => l.includes(txCbor) && !l.includes("..."));
235
+ expect(truncatedLog).toBeDefined();
236
+ expect(fullLog).toBeDefined();
237
+ });
238
+ });
239
+ describe("Sign", () => {
240
+ test("beforeSign hook is called before signing", async () => {
241
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog7");
242
+ const origLog = console.log;
243
+ const origWarn = console.warn;
244
+ console.log = () => {};
245
+ console.warn = () => {};
246
+ const {
247
+ HotWallet
248
+ } = await import("@blaze-cardano/sdk");
249
+ let beforeSignCalled = false;
250
+ selectResponses = ["sign", "cancel"];
251
+ const mockTx = createMockTx();
252
+ const hotWalletProto = HotWallet.prototype;
253
+ const mockWallet = Object.create(hotWalletProto);
254
+ mockWallet.getUsedAddresses = async () => [];
255
+ const mockBlaze = {
256
+ wallet: mockWallet,
257
+ signTransaction: async tx => ({
258
+ witnessSet: () => ({
259
+ vkeys: () => ({
260
+ size: () => 1,
261
+ toCore: () => [["vkey123", "sig123"]]
262
+ })
263
+ })
264
+ })
265
+ };
266
+ await sprinkle.TxDialog(mockBlaze, mockTx, {
267
+ beforeSign: async () => {
268
+ beforeSignCalled = true;
269
+ }
270
+ });
271
+ console.log = origLog;
272
+ console.warn = origWarn;
273
+ expect(beforeSignCalled).toBe(true);
274
+ });
275
+ test("Sign option hidden after signing", async () => {
276
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog-signed");
277
+ const origLog = console.log;
278
+ const origWarn = console.warn;
279
+ console.log = () => {};
280
+ console.warn = () => {};
281
+ const {
282
+ HotWallet
283
+ } = await import("@blaze-cardano/sdk");
284
+ selectResponses = ["sign", "cancel"];
285
+ const mockTx = createMockTx();
286
+ const hotWalletProto = HotWallet.prototype;
287
+ const mockWallet = Object.create(hotWalletProto);
288
+ mockWallet.getUsedAddresses = async () => [];
289
+ const mockBlaze = {
290
+ wallet: mockWallet,
291
+ signTransaction: async tx => ({
292
+ witnessSet: () => ({
293
+ vkeys: () => ({
294
+ size: () => 1,
295
+ toCore: () => [["vkey123", "sig123"]]
296
+ })
297
+ })
298
+ })
299
+ };
300
+ await sprinkle.TxDialog(mockBlaze, mockTx);
301
+ console.log = origLog;
302
+ console.warn = origWarn;
125
303
 
126
- // Create a mock that passes instanceof HotWallet check
127
- // We need to use Object.create to set the prototype
128
- const hotWalletProto = HotWallet.prototype;
129
- const mockWallet = Object.create(hotWalletProto);
130
- mockWallet.getUsedAddresses = async () => [];
131
- const mockBlaze = {
132
- wallet: mockWallet
133
- };
134
- await sprinkle.TxDialog(mockBlaze, mockTx);
135
- console.log = origLog;
136
- const menuCall = selectCalls[0];
137
- expect(menuCall).toBeDefined();
138
- const choiceNames = menuCall.choices.map(c => c.name);
139
- expect(choiceNames).toContain("Sign and submit transaction");
304
+ // First menu should have Sign, second should not
305
+ expect(selectCalls.length).toBe(2);
306
+ const firstMenuChoices = selectCalls[0].choices.map(c => c.name);
307
+ const secondMenuChoices = selectCalls[1].choices.map(c => c.name);
308
+ expect(firstMenuChoices).toContain("Sign with this wallet");
309
+ expect(secondMenuChoices).not.toContain("Sign with this wallet");
310
+ });
140
311
  });
141
- test("copy CBOR to clipboard works", async () => {
142
- const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog4");
143
- const origLog = console.log;
144
- const logs = [];
145
- console.log = (...args) => logs.push(args.join(" "));
312
+ describe("Return values", () => {
313
+ test("returns cancelled when user cancels", async () => {
314
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog-cancel");
315
+ const origLog = console.log;
316
+ console.log = () => {};
317
+ selectResponses = ["cancel"];
318
+ const mockTx = createMockTx();
319
+ const mockBlaze = {
320
+ wallet: {
321
+ getUsedAddresses: async () => []
322
+ }
323
+ };
324
+ const result = await sprinkle.TxDialog(mockBlaze, mockTx);
325
+ console.log = origLog;
326
+ expect(result.action).toBe("cancelled");
327
+ expect(result.tx).toBeDefined();
328
+ expect(result.txId).toBeUndefined();
329
+ });
330
+ test("returns signed when user signs then cancels", async () => {
331
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog-signed2");
332
+ const origLog = console.log;
333
+ const origWarn = console.warn;
334
+ console.log = () => {};
335
+ console.warn = () => {};
336
+ const {
337
+ HotWallet
338
+ } = await import("@blaze-cardano/sdk");
339
+ selectResponses = ["sign", "cancel"];
340
+ const mockTx = createMockTx();
341
+ const hotWalletProto = HotWallet.prototype;
342
+ const mockWallet = Object.create(hotWalletProto);
343
+ mockWallet.getUsedAddresses = async () => [];
344
+ const mockBlaze = {
345
+ wallet: mockWallet,
346
+ signTransaction: async tx => ({
347
+ witnessSet: () => ({
348
+ vkeys: () => ({
349
+ size: () => 1,
350
+ toCore: () => [["vkey123", "sig123"]]
351
+ })
352
+ })
353
+ })
354
+ };
355
+ const result = await sprinkle.TxDialog(mockBlaze, mockTx);
356
+ console.log = origLog;
357
+ console.warn = origWarn;
358
+ expect(result.action).toBe("signed");
359
+ expect(result.tx).toBeDefined();
360
+ });
361
+ test("returns submitted with txId on successful submit", async () => {
362
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog-submit");
363
+ const origLog = console.log;
364
+ console.log = () => {};
365
+ selectResponses = ["submit"];
366
+ confirmResponses = [true]; // Confirm submit with no signatures
146
367
 
147
- // Select "Copy CBOR to clipboard" (index 1), then Back (-1)
148
- selectResponses = [1, -1];
149
- const txCbor = "deadbeefcafebabe";
150
- const mockTx = {
151
- toCbor: () => txCbor,
152
- body: () => ({
153
- requiredSigners: () => null,
154
- certs: () => null,
155
- withdrawals: () => null
156
- })
157
- };
158
- const mockBlaze = {
159
- wallet: {
160
- getUsedAddresses: async () => []
161
- }
162
- };
163
- await sprinkle.TxDialog(mockBlaze, mockTx);
164
- console.log = origLog;
165
- expect(clipboardContent).toBe(txCbor);
166
- expect(logs.some(l => l.includes("copied to clipboard"))).toBe(true);
368
+ const mockTx = createMockTx();
369
+ const mockBlaze = {
370
+ wallet: {
371
+ getUsedAddresses: async () => []
372
+ },
373
+ submitTransaction: async () => "txhash123456"
374
+ };
375
+ const result = await sprinkle.TxDialog(mockBlaze, mockTx);
376
+ console.log = origLog;
377
+ expect(result.action).toBe("submitted");
378
+ expect(result.txId).toBe("txhash123456");
379
+ expect(result.tx).toBeDefined();
380
+ });
167
381
  });
168
- test("clipboard failure falls back to expanded view", async () => {
169
- const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog5");
170
- const origLog = console.log;
171
- const logs = [];
172
- console.log = (...args) => logs.push(args.join(" "));
173
- clipboardShouldFail = true;
382
+ describe("Submit warnings", () => {
383
+ test("warns when submitting with no signatures", async () => {
384
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog-warn");
385
+ const origLog = console.log;
386
+ console.log = () => {};
387
+
388
+ // First try to submit, decline warning, then cancel
389
+ selectResponses = ["submit", "cancel"];
390
+ confirmResponses = [false];
391
+ const mockTx = createMockTx();
392
+ const mockBlaze = {
393
+ wallet: {
394
+ getUsedAddresses: async () => []
395
+ }
396
+ };
397
+ const result = await sprinkle.TxDialog(mockBlaze, mockTx);
398
+ console.log = origLog;
174
399
 
175
- // Select "Copy CBOR to clipboard" (index 1),
176
- // It will fail and call showDialog again with expanded=true
177
- // Then Back (-1) from the expanded menu, then Back from outer
178
- selectResponses = [1, -1, -1];
179
- const txCbor = "a".repeat(100);
180
- const mockTx = {
181
- toCbor: () => txCbor,
182
- body: () => ({
183
- requiredSigners: () => null,
184
- certs: () => null,
185
- withdrawals: () => null
186
- })
187
- };
188
- const mockBlaze = {
189
- wallet: {
190
- getUsedAddresses: async () => []
191
- }
192
- };
193
- await sprinkle.TxDialog(mockBlaze, mockTx);
194
- console.log = origLog;
195
- expect(logs.some(l => l.includes("Failed to copy"))).toBe(true);
196
- // After failure it should show the full CBOR without truncation
197
- expect(logs.some(l => l.includes(txCbor) && !l.includes("..."))).toBe(true);
400
+ // Should have returned to menu, then cancelled
401
+ expect(result.action).toBe("cancelled");
402
+ expect(selectCalls.length).toBe(2);
403
+ });
198
404
  });
199
- test("expand CBOR shows full content", async () => {
200
- const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog6");
201
- const origLog = console.log;
202
- const logs = [];
203
- console.log = (...args) => logs.push(args.join(" "));
405
+ describe("Exit behavior", () => {
406
+ test("exits loop after successful submit", async () => {
407
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog-exit");
408
+ const origLog = console.log;
409
+ console.log = () => {};
410
+ selectResponses = ["submit"];
411
+ confirmResponses = [true];
412
+ const mockTx = createMockTx();
413
+ const mockBlaze = {
414
+ wallet: {
415
+ getUsedAddresses: async () => []
416
+ },
417
+ submitTransaction: async () => "txhash789"
418
+ };
419
+ const result = await sprinkle.TxDialog(mockBlaze, mockTx);
420
+ console.log = origLog;
204
421
 
205
- // Select "Expand CBOR" (index 0), then Back (-1) from expanded, then Back from outer
206
- selectResponses = [0, -1, -1];
207
- const txCbor = "b".repeat(100);
208
- const mockTx = {
209
- toCbor: () => txCbor,
210
- body: () => ({
211
- requiredSigners: () => null,
212
- certs: () => null,
213
- withdrawals: () => null
214
- })
215
- };
216
- const mockBlaze = {
217
- wallet: {
218
- getUsedAddresses: async () => []
219
- }
220
- };
221
- await sprinkle.TxDialog(mockBlaze, mockTx);
222
- console.log = origLog;
422
+ // Should only have one menu call - no loop after submit
423
+ expect(selectCalls.length).toBe(1);
424
+ expect(result.action).toBe("submitted");
425
+ });
426
+ });
427
+ describe("Required signers display", () => {
428
+ test("shows required signers when present", async () => {
429
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog-req");
430
+ const logs = [];
431
+ const origLog = console.log;
432
+ console.log = (...args) => logs.push(args.join(" "));
433
+ selectResponses = ["cancel"];
434
+ const mockTx = {
435
+ toCbor: () => "deadbeef",
436
+ body: () => ({
437
+ toCbor: () => "deadbeef",
438
+ requiredSigners: () => ({
439
+ values: () => [{
440
+ toString: () => "abc123def456"
441
+ }, {
442
+ toString: () => "789ghi012jkl"
443
+ }]
444
+ }),
445
+ certs: () => null,
446
+ withdrawals: () => null
447
+ }),
448
+ witnessSet: () => ({
449
+ vkeys: () => null
450
+ })
451
+ };
452
+ const mockBlaze = {
453
+ wallet: {
454
+ getUsedAddresses: async () => []
455
+ }
456
+ };
457
+ await sprinkle.TxDialog(mockBlaze, mockTx);
458
+ console.log = origLog;
223
459
 
224
- // First call shows truncated, second shows full
225
- const truncatedLog = logs.find(l => l.includes("...") && l.includes("Transaction CBOR:"));
226
- const fullLog = logs.find(l => l.includes(txCbor) && !l.includes("..."));
227
- expect(truncatedLog).toBeDefined();
228
- expect(fullLog).toBeDefined();
460
+ // Should show "X of Y required" format
461
+ expect(logs.some(l => l.includes("of") && l.includes("required"))).toBe(true);
462
+ // Should show required signers list
463
+ expect(logs.some(l => l.includes("Required signers:"))).toBe(true);
464
+ });
229
465
  });
230
- test("beforeSign hook is called before signing", async () => {
231
- const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog7");
232
- const origLog = console.log;
233
- const origWarn = console.warn;
234
- console.log = () => {};
235
- console.warn = () => {};
236
- const {
237
- HotWallet
238
- } = await import("@blaze-cardano/sdk");
239
- let beforeSignCalled = false;
466
+ describe("Import signatures", () => {
467
+ test("continues on empty CBOR input", async () => {
468
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog-import1");
469
+ const origLog = console.log;
470
+ const logs = [];
471
+ console.log = (...args) => logs.push(args.join(" "));
472
+ selectResponses = ["import", "cancel"];
473
+ inputResponses = [""]; // Empty input
240
474
 
241
- // Select "Sign and submit" (index 2 - after Expand and Copy), then Back
242
- selectResponses = [2, -1];
243
- const mockTx = {
244
- toCbor: () => "deadbeef",
245
- body: () => ({
246
- requiredSigners: () => null,
247
- certs: () => null,
248
- withdrawals: () => null
249
- })
250
- };
251
- const hotWalletProto = HotWallet.prototype;
252
- const mockWallet = Object.create(hotWalletProto);
253
- mockWallet.getUsedAddresses = async () => [];
254
- const mockBlaze = {
255
- wallet: mockWallet,
256
- signTransaction: async tx => tx,
257
- submitTransaction: async () => "txhash123"
258
- };
259
- await sprinkle.TxDialog(mockBlaze, mockTx, {
260
- beforeSign: async () => {
261
- beforeSignCalled = true;
262
- }
475
+ const mockTx = createMockTx();
476
+ const mockBlaze = {
477
+ wallet: {
478
+ getUsedAddresses: async () => []
479
+ }
480
+ };
481
+ const result = await sprinkle.TxDialog(mockBlaze, mockTx);
482
+ console.log = origLog;
483
+ expect(logs.some(l => l.includes("No CBOR provided"))).toBe(true);
484
+ expect(result.action).toBe("cancelled");
485
+ });
486
+ test("handles invalid CBOR gracefully", async () => {
487
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog-import2");
488
+ const origLog = console.log;
489
+ const origErr = console.error;
490
+ const logs = [];
491
+ const errors = [];
492
+ console.log = (...args) => logs.push(args.join(" "));
493
+ console.error = (...args) => errors.push(args.join(" "));
494
+ selectResponses = ["import", "cancel"];
495
+ inputResponses = ["not-valid-cbor"];
496
+ const mockTx = createMockTx();
497
+ const mockBlaze = {
498
+ wallet: {
499
+ getUsedAddresses: async () => []
500
+ }
501
+ };
502
+ const result = await sprinkle.TxDialog(mockBlaze, mockTx);
503
+ console.log = origLog;
504
+ console.error = origErr;
505
+ expect(errors.some(l => l.includes("Failed to import"))).toBe(true);
506
+ expect(result.action).toBe("cancelled");
263
507
  });
264
- console.log = origLog;
265
- console.warn = origWarn;
266
- expect(beforeSignCalled).toBe(true);
267
508
  });
268
509
  });
269
510
  //# sourceMappingURL=tx-dialog.test.js.map