Skip to content

Commit 095ce4f

Browse files
SiegklenesBeulkejhonyeduardo
authored andcommitted
feat(stepper): permite próximo passo assíncrono
Incluí capacidade de verificar se pode ir para o próximo passo com métodos assíncronos. Dessa forma, é possível gravar no servidor antes de seguir para o próximo passo Fixes #171
1 parent 246ef13 commit 095ce4f

File tree

3 files changed

+150
-68
lines changed

3 files changed

+150
-68
lines changed

projects/ui/src/lib/components/po-stepper/po-step/po-step.component.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { AfterContentInit, Component, ElementRef, Input } from '@angular/core';
2+
import { Observable } from 'rxjs';
23

34
import { uuid } from '../../../utils/util';
45

@@ -59,6 +60,8 @@ export class PoStepComponent implements AfterContentInit {
5960
*
6061
* Função chamada quando o próximo *step* for clicado ou quando o método `PoStepperComponent.next()` for chamado.
6162
* Ao retornar `true` define que esse *step* ficará ativo e o atual como concluído (*done*).
63+
* Também aceita funções que retornem `Observable<boolean>`. Ao retornar um `Observable<boolean>`,
64+
* garanta que esse `Observable` será completado.
6265
*
6366
* Ao ser disparada, a mesma receberá por parâmetro o `PoStepComponent` atual.
6467
*
@@ -72,7 +75,7 @@ export class PoStepComponent implements AfterContentInit {
7275
* </po-step>
7376
* ```
7477
*/
75-
@Input('p-can-active-next-step') canActiveNextStep: Function;
78+
@Input('p-can-active-next-step') canActiveNextStep: ((currentStep) => boolean) | ((currentStep) => Observable<boolean>);
7679

7780
/** Título que será exibido descrevendo o passo (*step*). */
7881
@Input('p-label') label: string;

projects/ui/src/lib/components/po-stepper/po-stepper.component.spec.ts

Lines changed: 106 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
import { ComponentFixture, TestBed } from '@angular/core/testing';
21
import { QueryList } from '@angular/core';
2+
import { ComponentFixture, fakeAsync, flush, TestBed } from '@angular/core/testing';
3+
import { of } from 'rxjs';
34

45
import { configureTestSuite } from './../../util-test/util-expect.spec';
56

7+
import { PoStepperOrientation } from './enums/po-stepper-orientation.enum';
8+
import { PoStepperStatus } from './enums/po-stepper-status.enum';
69
import { PoStepComponent } from './po-step/po-step.component';
710
import { PoStepperBaseComponent } from './po-stepper-base.component';
811
import { PoStepperComponent } from './po-stepper.component';
912
import { PoStepperModule } from './po-stepper.module';
10-
import { PoStepperOrientation } from './enums/po-stepper-orientation.enum';
11-
import { PoStepperStatus } from './enums/po-stepper-status.enum';
1213

1314
describe('PoStepperComponent:', () => {
1415
let component: PoStepperComponent;
@@ -236,7 +237,7 @@ describe('PoStepperComponent:', () => {
236237

237238
component['currentActiveStep'] = <any>poStepCurrentMock;
238239

239-
spyOn(component, <any>'allowNextStep').and.returnValue(true);
240+
spyOn(component, <any>'allowNextStep').and.returnValue(of(true));
240241
spyOnProperty(component, 'usePoSteps').and.returnValue(true);
241242

242243
const spyOncontrolStepsStatus = spyOn(component, <any>'controlStepsStatus');
@@ -256,7 +257,7 @@ describe('PoStepperComponent:', () => {
256257

257258
component['currentActiveStep'] = undefined;
258259

259-
spyOn(component, <any>'allowNextStep').and.returnValue(true);
260+
spyOn(component, <any>'allowNextStep').and.returnValue(of(true));
260261
spyOnProperty(component, 'usePoSteps').and.returnValue(true);
261262

262263
const spyOncontrolStepsStatus = spyOn(component, <any>'controlStepsStatus');
@@ -273,7 +274,7 @@ describe('PoStepperComponent:', () => {
273274

274275
component.step = 1;
275276

276-
const spyAllowNextStep = spyOn(component, <any> 'allowNextStep').and.returnValue(false);
277+
const spyAllowNextStep = spyOn(component, <any> 'allowNextStep').and.returnValue(of(false));
277278
const spyOnChangeStep = spyOn(component.onChangeStep, 'emit');
278279

279280
component.changeStep(stepNumber);
@@ -294,20 +295,23 @@ describe('PoStepperComponent:', () => {
294295
expect(spyOnChangeStep).not.toHaveBeenCalled();
295296
});
296297

297-
it('changeStep: should call `onChangeStep.emit` if `stepIndex` param is diff than `step` and `allowNextStep` return true', () => {
298-
const stepIndex = 3;
299-
const stepNumber = 4;
298+
it('changeStep: should call `onChangeStep.emit` if `stepIndex` param is diff than `step` and `allowNextStep` return true',
299+
fakeAsync(() => {
300+
const stepIndex = 3;
301+
const stepNumber = 4;
300302

301-
spyOnProperty(component, 'currentStepIndex').and.returnValue(1);
303+
spyOnProperty(component, 'currentStepIndex').and.returnValue(1);
302304

303-
const spyAllowNextStep = spyOn(component, <any> 'allowNextStep').and.returnValue(true);
304-
const spyOnChangeStep = spyOn(component.onChangeStep, 'emit');
305+
const spyAllowNextStep = spyOn(component, <any> 'allowNextStep').and.returnValue(of(true));
306+
const spyOnChangeStep = spyOn(component.onChangeStep, 'emit');
305307

306-
component.changeStep(stepIndex);
308+
component.changeStep(stepIndex);
309+
flush();
307310

308-
expect(spyAllowNextStep).toHaveBeenCalledWith(stepIndex);
309-
expect(spyOnChangeStep).toHaveBeenCalledWith(stepNumber);
310-
});
311+
expect(spyAllowNextStep).toHaveBeenCalledWith(stepIndex);
312+
expect(spyOnChangeStep).toHaveBeenCalledWith(stepNumber);
313+
})
314+
);
311315

312316
it('onStepActive: should set `currentActiveStep` to `currentActiveStep`', () => {
313317
component['currentActiveStep'] = undefined;
@@ -381,15 +385,18 @@ describe('PoStepperComponent:', () => {
381385
expect(component.changeStep).not.toHaveBeenCalled();
382386
});
383387

384-
it('allowNextStep: should return true if `sequential` is false', () => {
388+
it('allowNextStep: should return true if `sequential` is false', (done: DoneFn) => {
385389
const stepNumber = 1;
386390

387391
component.sequential = false;
388392

389-
expect(component['allowNextStep'](stepNumber)).toBe(true);
393+
component['allowNextStep'](stepNumber).subscribe(value => {
394+
expect(value).toBe(true);
395+
done();
396+
});
390397
});
391398

392-
it('allowNextStep: should return false if `sequential` is true', () => {
399+
it('allowNextStep: should return false if `sequential` is true', (done: DoneFn) => {
393400
const stepNumber = 3;
394401

395402
component.sequential = true;
@@ -398,76 +405,125 @@ describe('PoStepperComponent:', () => {
398405
component.steps.forEach(step => step.status = PoStepperStatus.Default);
399406
component.step = 1;
400407

401-
expect(component['allowNextStep'](stepNumber)).toBe(false);
408+
component['allowNextStep'](stepNumber).subscribe(value => {
409+
expect(value).toBe(false);
410+
done();
411+
});
402412
});
403413

404-
it('allowNextStep: should return true if `sequential` is true and every steps are status done', () => {
414+
it('allowNextStep: should return true if `sequential` is true and every steps are status done', (done: DoneFn) => {
405415
const stepNumber = 1;
406416

407417
component.sequential = true;
408418

409419
component.steps = poSteps.map(step => ({ ...step, status: PoStepperStatus.Done }));
410420

411-
expect(component['allowNextStep'](stepNumber)).toBe(true);
421+
component['allowNextStep'](stepNumber).subscribe(value => {
422+
expect(value).toBe(true);
423+
done();
424+
});
412425
});
413426

414-
it('allowNextStep: should return true if `sequential`, `usePoSteps`, `isBeforeStep` are true', () => {
427+
it('allowNextStep: should return true if `sequential`, `usePoSteps`, `isBeforeStep` are true', (done: DoneFn) => {
415428
component.sequential = true;
416429
const nextStepIndex = 1;
417430
spyOnProperty(component, 'usePoSteps').and.returnValue(true);
418431

419432
const spyOnIsBeforeStep = spyOn(component, <any>'isBeforeStep').and.returnValue(true);
420433

421-
expect(component['allowNextStep'](nextStepIndex)).toBe(true);
434+
component['allowNextStep'](nextStepIndex).subscribe(value => {
435+
expect(value).toBe(true);
436+
done();
437+
});
422438
expect(spyOnIsBeforeStep).toHaveBeenCalledWith(nextStepIndex);
423439
});
424440

425441
it(`allowNextStep: should return true if 'sequential', 'usePoSteps' and 'canActiveNextStep' are true and
426-
'isBeforeStep' is false`, () => {
442+
'isBeforeStep' is false`, (done: DoneFn) => {
427443
component.sequential = true;
428444
const nextStepIndex = 1;
429445
spyOnProperty(component, 'usePoSteps').and.returnValue(true);
430446

431-
const spyOnCanActiveNextStep = spyOn(component, <any>'canActiveNextStep').and.returnValue(true);
447+
const spyOnCanActiveNextStep = spyOn(component, <any>'canActiveNextStep').and.returnValue(of(true));
432448
spyOn(component, <any>'isBeforeStep').and.returnValue(false);
433449

434-
expect(component['allowNextStep'](nextStepIndex)).toBe(true);
450+
component['allowNextStep'](nextStepIndex).subscribe(value => {
451+
expect(value).toBe(true);
452+
done();
453+
});
435454
expect(spyOnCanActiveNextStep).toHaveBeenCalledWith(component['currentActiveStep']);
436455
});
437456

438-
it('allowNextStep: should return false if `isBeforeStep` and `canActiveNextStep` are false', () => {
457+
it('allowNextStep: should return false if `isBeforeStep` and `canActiveNextStep` are false', (done: DoneFn) => {
439458
component.sequential = true;
440459
const nextStepIndex = 1;
441460
spyOnProperty(component, 'usePoSteps').and.returnValue(true);
442461

443-
spyOn(component, <any>'canActiveNextStep').and.returnValue(false);
462+
spyOn(component, <any>'canActiveNextStep').and.returnValue(of(false));
444463
spyOn(component, <any>'isBeforeStep').and.returnValue(false);
445464

446-
expect(component['allowNextStep'](nextStepIndex)).toBe(false);
465+
component['allowNextStep'](nextStepIndex).subscribe(value => {
466+
expect(value).toBe(false);
467+
done();
468+
});
447469
});
448470

449-
it('canActiveNextStep: should return true if `currentActiveStep.canActiveNextStep` function return true', () => {
450-
const currentActiveStep = { canActiveNextStep: () => true };
471+
it('canActiveNextStep: should return true if `currentActiveStep.canActiveNextStep` function return true', (done: DoneFn) => {
472+
const currentActiveStep = <PoStepComponent>{ canActiveNextStep: currentStep => true };
451473

452-
const result = component['canActiveNextStep'](currentActiveStep);
474+
component['canActiveNextStep'](currentActiveStep).subscribe(result => {
475+
expect(result).toBe(true);
476+
done();
477+
});
478+
});
453479

454-
expect(result).toBe(true);
480+
it('canActiveNextStep: should return false if `currentActiveStep.canActiveNextStep` function return false', (done: DoneFn) => {
481+
const currentActiveStep = <PoStepComponent>{ canActiveNextStep: currentStep => false, status: PoStepperStatus.Default };
482+
483+
component['canActiveNextStep'](currentActiveStep).subscribe(result => {
484+
expect(result).toBe(false);
485+
done();
486+
});
455487
});
456488

457-
it('canActiveNextStep: should return false if `currentActiveStep.canActiveNextStep` function return false', () => {
458-
const currentActiveStep = { canActiveNextStep: () => false, status: PoStepperStatus.Default };
489+
it(`canActiveNextStep: should return true if 'currentActiveStep.canActiveNextStep' function
490+
return an observable with true value`, (done: DoneFn) => {
491+
const currentActiveStep = <PoStepComponent>{ canActiveNextStep: currentStep => of(true) };
492+
493+
component['canActiveNextStep'](currentActiveStep).subscribe(result => {
494+
expect(result).toBe(true);
495+
done();
496+
});
497+
});
459498

460-
const result = component['canActiveNextStep'](currentActiveStep);
499+
it(`canActiveNextStep: should return false if 'currentActiveStep.canActiveNextStep' function
500+
return an observable with false value`, (done: DoneFn) => {
501+
const currentActiveStep = <PoStepComponent>{ canActiveNextStep: currentStep => of(false), status: PoStepperStatus.Default };
461502

462-
expect(result).toBe(false);
503+
component['canActiveNextStep'](currentActiveStep).subscribe(result => {
504+
expect(result).toBe(false);
505+
done();
506+
});
463507
});
464508

465-
it('canActiveNextStep: should return true if `currentActiveStep` is undefined', () => {
466-
const currentActiveStep = undefined;
509+
it(`canActiveNextStep: should throw error if return type is not a boolean of function`, () => {
510+
const currentActiveStep = <PoStepComponent>{
511+
label: 'po-label',
512+
canActiveNextStep: currentStep => 10 as any,
513+
status: PoStepperStatus.Default
514+
};
467515

468-
const result = component['canActiveNextStep'](currentActiveStep);
516+
expect(() => component['canActiveNextStep'](currentActiveStep).subscribe(() => expect('Not have been called').toBe('this function')))
517+
.toThrowError(`Expected step po-label canActiveNextStep function to return either a boolean or Observable`);
518+
});
469519

470-
expect(result).toBe(true);
520+
it('canActiveNextStep: should return true if `currentActiveStep` is undefined', (done: DoneFn) => {
521+
const currentActiveStep = undefined;
522+
523+
component['canActiveNextStep'](currentActiveStep).subscribe(result => {
524+
expect(result).toBe(true);
525+
done();
526+
});
471527
});
472528

473529
it(`controlStepsStatus: shouldn't call 'isBeforeStep', 'setStepAsActive', 'setNextStepAsDefault' and
@@ -821,7 +877,7 @@ describe('PoStepperComponent:', () => {
821877
});
822878

823879
it(`should initialize first step as active, second as default and others as disabled,
824-
and should set step clicked as 'active', the previous as 'done', the next as 'default'and the last as 'disabled'.`, () => {
880+
and should set step clicked as 'active', the previous as 'done', the next as 'default'and the last as 'disabled'.`, fakeAsync(() => {
825881
const poStepsMock = new QueryList<PoStepComponent>();
826882
const poStepList = [
827883
{ id: '1', label: 'Step 1', status: PoStepperStatus.Active },
@@ -842,6 +898,7 @@ describe('PoStepperComponent:', () => {
842898
spyOnProperty(component, 'usePoSteps').and.returnValue(true);
843899

844900
fixture.detectChanges();
901+
flush();
845902

846903
const steps = nativeElement.querySelectorAll('.po-stepper-step');
847904

@@ -854,16 +911,17 @@ describe('PoStepperComponent:', () => {
854911
steps[1].dispatchEvent(eventClick);
855912

856913
fixture.detectChanges();
914+
flush();
857915

858916
expect(steps[0].classList.contains('po-stepper-step-done')).toBeTruthy();
859917
expect(steps[1].classList.contains('po-stepper-step-active')).toBeTruthy();
860918
expect(steps[2].classList.contains('po-stepper-step-default')).toBeTruthy();
861919
expect(steps[3].classList.contains('po-stepper-step-disabled')).toBeTruthy();
862920
expect(steps[4].classList.contains('po-stepper-step-disabled')).toBeTruthy();
863-
});
921+
}));
864922

865923
it(`should initialize step 4 as active, step 5 as default and others as disabled,
866-
and should set step clicked as 'active', the previous as 'done', the next as 'default'and the last as 'disabled'.`, () => {
924+
and should set step clicked as 'active', the previous as 'done', the next as 'default'and the last as 'disabled'.`, fakeAsync(() => {
867925
const poStepsMock = new QueryList<PoStepComponent>();
868926
const poStepList = [
869927
{ id: '1', label: 'Step 1', status: PoStepperStatus.Done },
@@ -884,6 +942,7 @@ describe('PoStepperComponent:', () => {
884942
spyOnProperty(component, 'usePoSteps').and.returnValue(true);
885943

886944
fixture.detectChanges();
945+
flush();
887946

888947
const steps = nativeElement.querySelectorAll('.po-stepper-step');
889948

@@ -896,13 +955,14 @@ describe('PoStepperComponent:', () => {
896955
steps[1].dispatchEvent(eventClick);
897956

898957
fixture.detectChanges();
958+
flush();
899959

900960
expect(steps[0].classList.contains('po-stepper-step-done')).toBeTruthy();
901961
expect(steps[1].classList.contains('po-stepper-step-active')).toBeTruthy();
902962
expect(steps[2].classList.contains('po-stepper-step-default')).toBeTruthy();
903963
expect(steps[3].classList.contains('po-stepper-step-disabled')).toBeTruthy();
904964
expect(steps[4].classList.contains('po-stepper-step-disabled')).toBeTruthy();
905-
});
965+
}));
906966

907967
});
908968

0 commit comments

Comments
 (0)