@block-auth.io/angular 0.2.10 → 0.3.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.
- package/.turbo/turbo-build.log +40 -37
- package/.turbo/turbo-lint.log +4 -0
- package/.turbo/turbo-test.log +102 -0
- package/.turbo/turbo-typecheck.log +19 -0
- package/CHANGELOG.md +113 -0
- package/package.json +3 -3
- package/src/lib/components/txns/internals/permissions-required-modal.component.test.ts +9 -0
- package/src/lib/components/txns/internals/permissions-required-modal.component.ts +3 -3
- package/src/lib/components/txns/internals/phone-input.component.test.ts +119 -0
- package/src/lib/components/txns/internals/phone-input.component.ts +301 -0
- package/src/lib/components/txns/internals/solicited-data-modal.component.test.ts +561 -4
- package/src/lib/components/txns/internals/solicited-data-modal.component.ts +324 -129
- package/src/lib/components/txns/internals/txn-type.component.ts +2 -2
- package/src/lib/components/txns/internals/txn.component.test.ts +345 -0
- package/src/lib/components/txns/internals/txn.component.ts +157 -74
- package/src/lib/components/txns/txns-overview.component.test.ts +270 -2
- package/src/lib/components/txns/txns-overview.component.ts +153 -23
- package/src/lib/primitives/arrow-icon.component.test.ts +13 -0
- package/src/lib/primitives/arrow-icon.component.ts +8 -1
- package/src/public-api.ts +1 -0
|
@@ -165,9 +165,38 @@ describe('Txn', () => {
|
|
|
165
165
|
expect(client.txns.accept).toHaveBeenCalledWith({
|
|
166
166
|
lifetime: 3600,
|
|
167
167
|
userParams: { 'users-email': 'a@b.com' },
|
|
168
|
+
grantedParams: [],
|
|
169
|
+
revokedParams: [],
|
|
168
170
|
});
|
|
169
171
|
});
|
|
170
172
|
|
|
173
|
+
it('ACCEPT envía grantedParams/revokedParams y limpia el error previo de aceptación', async () => {
|
|
174
|
+
const client = makeClient();
|
|
175
|
+
client.state.$ui.setKey('acceptTxnError', 'Required data not authorized');
|
|
176
|
+
@Component({
|
|
177
|
+
standalone: true,
|
|
178
|
+
imports: [TxnComponent],
|
|
179
|
+
template: `<ba-txn
|
|
180
|
+
[selectedTxnPreParams]="{ appKey: 'k', to: '0xabc' }"
|
|
181
|
+
[grantedParams]="['users-email']"
|
|
182
|
+
[revokedParams]="['users-lastname']"
|
|
183
|
+
/>`,
|
|
184
|
+
})
|
|
185
|
+
class Host {}
|
|
186
|
+
await render(Host, { providers: [provideBlockAuthClientForTest(client)] });
|
|
187
|
+
await fireEvent.click(screen.getByTestId('txn-pptc-toggle'));
|
|
188
|
+
await fireEvent.click(screen.getByText('ACCEPT'));
|
|
189
|
+
|
|
190
|
+
expect(client.txns.accept).toHaveBeenCalledWith({
|
|
191
|
+
lifetime: 3600,
|
|
192
|
+
userParams: {},
|
|
193
|
+
grantedParams: ['users-email'],
|
|
194
|
+
revokedParams: ['users-lastname'],
|
|
195
|
+
});
|
|
196
|
+
expect(client.state.$ui.get().acceptTxnError).toBeNull();
|
|
197
|
+
expect(client.state.$ui.get().isAcceptingTxn).toBe(true);
|
|
198
|
+
});
|
|
199
|
+
|
|
171
200
|
it('emite permissionsRequiredClicked/solicitedDataClicked (solo si hasSolicitedData)', async () => {
|
|
172
201
|
const onPerm = vi.fn();
|
|
173
202
|
const onSol = vi.fn();
|
|
@@ -232,4 +261,320 @@ describe('Txn', () => {
|
|
|
232
261
|
) as HTMLInputElement;
|
|
233
262
|
expect(timeInput.value).toBe('02:00:00');
|
|
234
263
|
});
|
|
264
|
+
|
|
265
|
+
const lifetimeSlider = () =>
|
|
266
|
+
document.getElementById('txn-lifetime-slider-slider') as HTMLInputElement;
|
|
267
|
+
|
|
268
|
+
it('"Quiero configurar el tiempo de mi sesión" abre el selector en un popup', async () => {
|
|
269
|
+
await render(TxnComponent, {
|
|
270
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
271
|
+
});
|
|
272
|
+
expect(screen.queryByRole('dialog')).toBeNull();
|
|
273
|
+
expect(lifetimeSlider()).toBeNull();
|
|
274
|
+
await fireEvent.click(
|
|
275
|
+
screen.getByRole('button', {
|
|
276
|
+
name: 'I want to configure my session time',
|
|
277
|
+
}),
|
|
278
|
+
);
|
|
279
|
+
const dialog = screen.getByRole('dialog', {
|
|
280
|
+
name: 'I want to configure my session time',
|
|
281
|
+
});
|
|
282
|
+
expect(dialog.classList.contains('bal-ba-glass-panel')).toBe(true);
|
|
283
|
+
expect(dialog.contains(lifetimeSlider())).toBe(true);
|
|
284
|
+
expect(lifetimeSlider().value).toBe('3600');
|
|
285
|
+
expect(
|
|
286
|
+
document.getElementById('btn-configure-session-time'),
|
|
287
|
+
).not.toBeNull();
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('Aceptar cierra el popup y conserva el tiempo elegido junto al enlace', async () => {
|
|
291
|
+
await render(TxnComponent, {
|
|
292
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
293
|
+
});
|
|
294
|
+
expect(document.getElementById('txn-lifetime-value')).toBeNull();
|
|
295
|
+
await fireEvent.click(
|
|
296
|
+
document.getElementById('btn-configure-session-time')!,
|
|
297
|
+
);
|
|
298
|
+
await fireEvent.input(lifetimeSlider(), { target: { value: '7200' } });
|
|
299
|
+
await fireEvent.click(document.getElementById('txn-lifetime-accept')!);
|
|
300
|
+
expect(screen.queryByRole('dialog')).toBeNull();
|
|
301
|
+
expect(document.getElementById('txn-lifetime-value')?.textContent).toBe(
|
|
302
|
+
'02:00:00',
|
|
303
|
+
);
|
|
304
|
+
expect(
|
|
305
|
+
document
|
|
306
|
+
.querySelector('#btn-configure-session-time p')
|
|
307
|
+
?.contains(document.getElementById('txn-lifetime-value')),
|
|
308
|
+
).toBe(true);
|
|
309
|
+
await fireEvent.click(
|
|
310
|
+
document.getElementById('btn-configure-session-time')!,
|
|
311
|
+
);
|
|
312
|
+
expect(lifetimeSlider().value).toBe('7200');
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('cerrar con la x o fuera del popup conserva el tiempo elegido', async () => {
|
|
316
|
+
const { container } = await render(TxnComponent, {
|
|
317
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
318
|
+
});
|
|
319
|
+
await fireEvent.click(
|
|
320
|
+
document.getElementById('btn-configure-session-time')!,
|
|
321
|
+
);
|
|
322
|
+
await fireEvent.input(lifetimeSlider(), { target: { value: '120' } });
|
|
323
|
+
await fireEvent.click(screen.getByRole('button', { name: 'Close' }));
|
|
324
|
+
expect(screen.queryByRole('dialog')).toBeNull();
|
|
325
|
+
expect(document.getElementById('txn-lifetime-value')?.textContent).toBe(
|
|
326
|
+
'00:02:00',
|
|
327
|
+
);
|
|
328
|
+
await fireEvent.click(
|
|
329
|
+
document.getElementById('btn-configure-session-time')!,
|
|
330
|
+
);
|
|
331
|
+
await fireEvent.input(lifetimeSlider(), { target: { value: '60' } });
|
|
332
|
+
await fireEvent.click(container.querySelector('.bal-ba-glass-backdrop')!);
|
|
333
|
+
expect(screen.queryByRole('dialog')).toBeNull();
|
|
334
|
+
expect(document.getElementById('txn-lifetime-value')?.textContent).toBe(
|
|
335
|
+
'00:01:00',
|
|
336
|
+
);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('traduce el popup del tiempo de sesión', async () => {
|
|
340
|
+
setLocale('es');
|
|
341
|
+
await render(TxnComponent, {
|
|
342
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
343
|
+
});
|
|
344
|
+
await fireEvent.click(
|
|
345
|
+
document.getElementById('btn-configure-session-time')!,
|
|
346
|
+
);
|
|
347
|
+
expect(
|
|
348
|
+
screen.getByRole('dialog', {
|
|
349
|
+
name: 'Quiero configurar el tiempo de mi sesión',
|
|
350
|
+
}),
|
|
351
|
+
).toBeDefined();
|
|
352
|
+
expect(screen.getByRole('button', { name: 'Cerrar' })).toBeDefined();
|
|
353
|
+
expect(
|
|
354
|
+
document.getElementById('txn-lifetime-accept')?.textContent?.trim(),
|
|
355
|
+
).toBe('ACEPTAR');
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('ACCEPT envía el lifetime elegido en el popup del tiempo de sesión', async () => {
|
|
359
|
+
const client = makeClient();
|
|
360
|
+
await render(TxnComponent, {
|
|
361
|
+
inputs: { selectedTxnPreParams: { appKey: 'app', to: '0xabc' } },
|
|
362
|
+
providers: [provideBlockAuthClientForTest(client)],
|
|
363
|
+
});
|
|
364
|
+
await fireEvent.click(
|
|
365
|
+
document.getElementById('btn-configure-session-time')!,
|
|
366
|
+
);
|
|
367
|
+
await fireEvent.input(lifetimeSlider(), { target: { value: '5400' } });
|
|
368
|
+
await fireEvent.click(document.getElementById('txn-lifetime-accept')!);
|
|
369
|
+
await fireEvent.click(screen.getByTestId('txn-pptc-toggle'));
|
|
370
|
+
await fireEvent.click(document.getElementById('btn-txn-accept')!);
|
|
371
|
+
expect(client.txns.accept).toHaveBeenCalledWith(
|
|
372
|
+
expect.objectContaining({ lifetime: 5400 }),
|
|
373
|
+
);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const visibleArrows = (container: Element) =>
|
|
377
|
+
Array.from(container.querySelectorAll('[aria-hidden="true"]')).filter(
|
|
378
|
+
(el) =>
|
|
379
|
+
el.querySelector('.bal-ba-arrow') &&
|
|
380
|
+
!el.className.includes('bas-invisible'),
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
it('"Datos solicitados" queda desactivado cuando no hay datos', async () => {
|
|
384
|
+
const onSol = vi.fn();
|
|
385
|
+
@Component({
|
|
386
|
+
standalone: true,
|
|
387
|
+
imports: [TxnComponent],
|
|
388
|
+
template: `<ba-txn (solicitedDataClicked)="s()" />`,
|
|
389
|
+
})
|
|
390
|
+
class Host {
|
|
391
|
+
s = onSol;
|
|
392
|
+
}
|
|
393
|
+
await render(Host, {
|
|
394
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
395
|
+
});
|
|
396
|
+
const btn = screen
|
|
397
|
+
.getByText('Solicited data')
|
|
398
|
+
.closest('button') as HTMLButtonElement;
|
|
399
|
+
expect(btn.disabled).toBe(true);
|
|
400
|
+
await fireEvent.click(btn);
|
|
401
|
+
expect(onSol).not.toHaveBeenCalled();
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('"Permisos requeridos" y "Datos solicitados" comparten el estilo glass de ancho completo y una línea', async () => {
|
|
405
|
+
const { container } = await render(TxnComponent, {
|
|
406
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
407
|
+
});
|
|
408
|
+
const permissionsBtn = container.querySelector(
|
|
409
|
+
'#btn-txn-permissions-required',
|
|
410
|
+
) as HTMLButtonElement;
|
|
411
|
+
const solicitedDataBtn = container.querySelector(
|
|
412
|
+
'#btn-txn-solicited-data',
|
|
413
|
+
) as HTMLButtonElement;
|
|
414
|
+
for (const btn of [permissionsBtn, solicitedDataBtn]) {
|
|
415
|
+
expect(btn.classList.contains('bal-ba-glass-btn')).toBe(true);
|
|
416
|
+
expect(
|
|
417
|
+
btn.querySelector('.bal-ba-glass-btn-label')?.textContent?.trim(),
|
|
418
|
+
).toBeTruthy();
|
|
419
|
+
expect(btn.querySelector('.bal-ba-arrow')).toBeNull();
|
|
420
|
+
}
|
|
421
|
+
expect(solicitedDataBtn.disabled).toBe(true);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('las flechas de "Datos solicitados" van superpuestas a los lados, fuera del botón', async () => {
|
|
425
|
+
const { container } = await render(TxnComponent, {
|
|
426
|
+
inputs: { hasSolicitedData: true },
|
|
427
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
428
|
+
});
|
|
429
|
+
const row = container.querySelector('#btn-txn-solicited-data')
|
|
430
|
+
?.parentElement as HTMLElement;
|
|
431
|
+
expect(
|
|
432
|
+
row.querySelector('.bal-ba-glass-btn-arrow-left .bal-ba-arrow-right'),
|
|
433
|
+
).toBeTruthy();
|
|
434
|
+
expect(
|
|
435
|
+
row.querySelector('.bal-ba-glass-btn-arrow-right .bal-ba-arrow-left'),
|
|
436
|
+
).toBeTruthy();
|
|
437
|
+
expect(container.querySelectorAll('.bal-ba-arrow')).toHaveLength(2);
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('muestra siempre las flechas de "Datos solicitados" mientras falten datos', async () => {
|
|
441
|
+
const { container } = await render(TxnComponent, {
|
|
442
|
+
inputs: { hasSolicitedData: true, allRequiredFilled: false },
|
|
443
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
444
|
+
});
|
|
445
|
+
expect(visibleArrows(container)).toHaveLength(2);
|
|
446
|
+
expect(
|
|
447
|
+
screen.getByText('Solicited data').closest('button')?.className,
|
|
448
|
+
).toContain('bal-ba-glass-btn-pending');
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('oculta las flechas cuando no hay nada pendiente', async () => {
|
|
452
|
+
const { container } = await render(TxnComponent, {
|
|
453
|
+
inputs: { hasSolicitedData: true },
|
|
454
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
455
|
+
});
|
|
456
|
+
expect(visibleArrows(container)).toHaveLength(0);
|
|
457
|
+
expect(
|
|
458
|
+
screen.getByText('Solicited data').closest('button')?.className,
|
|
459
|
+
).not.toContain('bal-ba-glass-btn-pending');
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it('solicitedDataPending manda sobre el cálculo por defecto', async () => {
|
|
463
|
+
const { container } = await render(TxnComponent, {
|
|
464
|
+
inputs: {
|
|
465
|
+
hasSolicitedData: true,
|
|
466
|
+
allRequiredFilled: false,
|
|
467
|
+
solicitedDataPending: false,
|
|
468
|
+
},
|
|
469
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
470
|
+
});
|
|
471
|
+
expect(visibleArrows(container)).toHaveLength(0);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it('aplica el estilo glass a la casilla de política y a RECHAZAR/ACEPTAR', async () => {
|
|
475
|
+
const { container } = await render(TxnComponent, {
|
|
476
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
477
|
+
});
|
|
478
|
+
const consent = screen.getByTestId('txn-pptc-toggle');
|
|
479
|
+
expect(consent.className).toContain('bal-ba-glass-consent');
|
|
480
|
+
expect(consent.className).not.toContain('bal-ba-glass-consent-checked');
|
|
481
|
+
await fireEvent.click(consent);
|
|
482
|
+
expect(consent.className).toContain('bal-ba-glass-consent-checked');
|
|
483
|
+
expect(container.querySelector('#btn-txn-reject')?.className).toContain(
|
|
484
|
+
'bal-ba-glass-action-reject',
|
|
485
|
+
);
|
|
486
|
+
expect(container.querySelector('#btn-txn-accept')?.className).toContain(
|
|
487
|
+
'bal-ba-glass-action-accept',
|
|
488
|
+
);
|
|
489
|
+
await fireEvent.click(
|
|
490
|
+
screen.getByText('I want to configure my session time'),
|
|
491
|
+
);
|
|
492
|
+
expect(container.querySelector('.bal-ba-glass-time')).not.toBeNull();
|
|
493
|
+
expect(container.querySelector('.bal-ba-glass-slider')).not.toBeNull();
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
it('al pasar por ACEPTAR deshabilitado sin política aceptada vibra la casilla', async () => {
|
|
497
|
+
const { container } = await render(TxnComponent, {
|
|
498
|
+
inputs: { hasSolicitedData: true, allRequiredFilled: false },
|
|
499
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
500
|
+
});
|
|
501
|
+
await fireEvent.mouseEnter(screen.getByTestId('txn-accept-wrapper'));
|
|
502
|
+
expect(screen.getByTestId('txn-pptc-toggle').className).toContain(
|
|
503
|
+
'bal-ba-head-shake',
|
|
504
|
+
);
|
|
505
|
+
expect(
|
|
506
|
+
screen.getByText('Solicited data').closest('button')?.className,
|
|
507
|
+
).not.toContain('bal-ba-head-shake');
|
|
508
|
+
expect(visibleArrows(container)).toHaveLength(2);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it('al pasar por ACEPTAR deshabilitado con datos pendientes vibra "Datos solicitados" y mantiene sus flechas', async () => {
|
|
512
|
+
const { container } = await render(TxnComponent, {
|
|
513
|
+
inputs: { hasSolicitedData: true, allRequiredFilled: false },
|
|
514
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
515
|
+
});
|
|
516
|
+
await fireEvent.click(screen.getByTestId('txn-pptc-toggle'));
|
|
517
|
+
const wrapper = screen.getByTestId('txn-accept-wrapper');
|
|
518
|
+
await fireEvent.mouseEnter(wrapper);
|
|
519
|
+
const solicitedDataBtn = screen
|
|
520
|
+
.getByText('Solicited data')
|
|
521
|
+
.closest('button');
|
|
522
|
+
expect(solicitedDataBtn?.className).toContain('bal-ba-head-shake');
|
|
523
|
+
expect(
|
|
524
|
+
screen.getByText('Permissions required').closest('button')?.className,
|
|
525
|
+
).not.toContain('bal-ba-head-shake');
|
|
526
|
+
expect(screen.getByTestId('txn-pptc-toggle').className).not.toContain(
|
|
527
|
+
'bal-ba-head-shake',
|
|
528
|
+
);
|
|
529
|
+
expect(visibleArrows(container)).toHaveLength(2);
|
|
530
|
+
await fireEvent.mouseLeave(wrapper);
|
|
531
|
+
expect(solicitedDataBtn?.className).not.toContain('bal-ba-head-shake');
|
|
532
|
+
expect(visibleArrows(container)).toHaveLength(2);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
it('el foco en el contenedor de ACEPTAR deshabilitado también muestra la pista', async () => {
|
|
536
|
+
await render(TxnComponent, {
|
|
537
|
+
inputs: { hasSolicitedData: true, allRequiredFilled: false },
|
|
538
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
539
|
+
});
|
|
540
|
+
const wrapper = screen.getByTestId('txn-accept-wrapper');
|
|
541
|
+
await fireEvent.focusIn(wrapper);
|
|
542
|
+
expect(screen.getByTestId('txn-pptc-toggle').className).toContain(
|
|
543
|
+
'bal-ba-head-shake',
|
|
544
|
+
);
|
|
545
|
+
await fireEvent.focusOut(wrapper);
|
|
546
|
+
expect(screen.getByTestId('txn-pptc-toggle').className).not.toContain(
|
|
547
|
+
'bal-ba-head-shake',
|
|
548
|
+
);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it('no muestra pistas al pasar por ACEPTAR habilitado', async () => {
|
|
552
|
+
const { container } = await render(TxnComponent, {
|
|
553
|
+
inputs: { hasSolicitedData: true },
|
|
554
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
555
|
+
});
|
|
556
|
+
await fireEvent.click(screen.getByTestId('txn-pptc-toggle'));
|
|
557
|
+
await fireEvent.mouseEnter(screen.getByTestId('txn-accept-wrapper'));
|
|
558
|
+
expect(container.querySelector('.bal-ba-head-shake')).toBeNull();
|
|
559
|
+
expect(visibleArrows(container)).toHaveLength(0);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it('"Permisos requeridos" es un botón accesible que emite su evento', async () => {
|
|
563
|
+
const onPerm = vi.fn();
|
|
564
|
+
@Component({
|
|
565
|
+
standalone: true,
|
|
566
|
+
imports: [TxnComponent],
|
|
567
|
+
template: `<ba-txn (permissionsRequiredClicked)="p()" />`,
|
|
568
|
+
})
|
|
569
|
+
class Host {
|
|
570
|
+
p = onPerm;
|
|
571
|
+
}
|
|
572
|
+
await render(Host, {
|
|
573
|
+
providers: [provideBlockAuthClientForTest(makeClient())],
|
|
574
|
+
});
|
|
575
|
+
await fireEvent.click(
|
|
576
|
+
screen.getByRole('button', { name: 'Permissions required' }),
|
|
577
|
+
);
|
|
578
|
+
expect(onPerm).toHaveBeenCalledOnce();
|
|
579
|
+
});
|
|
235
580
|
});
|