I'm trying to perform some tests in one of my components. I am using an observable which is piped to the paramMap of the ActivatedRoute in order to get a guid from the URL and using a switchMap where I assign a value from the response into a component variable. My view is using the observable to show the data on screen and all seems to be working fine when I serve the app via the cli.
Once I try to perform the tests for my component the ExpressionChangedAfterItHasBeenCheckedError shows in the Karma's browser tab.
I believe the error is caused because I am changing the value of the isBranded variable, but I need it to show the proper logo in the view.
My component code:
@Component({
selector: 'app-digital-payment',
templateUrl: './digital-payment.component.html',
styleUrls: ['./digital-payment.component.scss']
})
export class DigitalPaymentComponent implements OnInit {
cdnUrl = environment.cdnUrl;
isBranded = false;
payment$: any;
constructor(private service: DigitalPaymentService, private activeRoute: ActivatedRoute) {
}
ngOnInit() {
this.payment$ = this.activeRoute.paramMap.pipe(switchMap((params: any) => {
let guid = params.get('guid');
return this.service.checkPaymentStatus(guid).pipe(map((data: any) => {
this.isBranded = data.isBranded;
return data;
}));
}));
}
}
My view:
<header>
<a >
<img src="{{ cdnUrl }}/Image/not-branded-logo.svg" alt="Not Branded Logo" />
</a>
<a *ngIf="isBranded">
<img src="{{ cdnUrl }}/Image/branded-logo.svg" alt="Branded Logo" />
</a>
</header>
<section>
<div *ngIf="payment$ | async as payment; else loading">
<mat-icon *ngIf="payment.icon">{{ payment.icon }}</mat-icon>
<h1>{{ payment.heading }}</h1>
<p>{{ payment.text }}</p>
</div>
<ng-template #loading>
<h1 >Loading</h1>
<p>Please wait until your request has been executed.</p>
</ng-template>
</section>
My Service:
@Injectable({
providedIn: 'root'
})
export class DigitalPaymentService {
private apiUrl: string = 'digitalpaymentexternal/';
private statusBinding = {
Success: {
class: 'success',
icon: 'check_circle_outline',
heading: 'Successful',
text: 'Your payment plan has been confirmed.'
},
Failed: {
class: 'fail',
icon: 'highlight_off',
heading: 'Transaction failed',
text: 'Please try a different payment method.'
},
Invalid: {
class: 'fail',
icon: 'error_outline',
heading: 'Incorrect link',
text: 'This link is invalid or it has been expired.'
}
};
constructor(private requester: RequesterService) { }
checkPaymentStatus(guid): Observable<any> {
return this.requester.post<void>(this.apiUrl 'CheckPaymentStatus', guid).pipe(map((data: any) => {
return { ...this.statusBinding[data.status], isBranded: data.isBranded };
}));
}
}
And my component test code:
describe('DigitalPaymentComponent', () => {
let component: DigitalPaymentComponent;
let fixture: ComponentFixture<DigitalPaymentComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DigitalPaymentComponent],
providers: [
{
provide: DigitalPaymentService,
useValue: {
checkPaymentStatus: guid => of({ isBranded: true })
}
},
{
provide: ActivatedRoute,
useValue: {
paramMap: of(convertToParamMap({ guid: '00000000-0000-0000-0000-000000000000' }))
},
},
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DigitalPaymentComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
CodePudding user response:
Probable solution is to add this.cdr.detectChagnes();
inside ngOnInit()
or ngAfterViewInit()
. cdr
refers to ChangeDetectionRef
.
constructor(..., private cdr: ChangeDetectionRef) {}
ngOnInit() {
...
// for karma test to avoid ExpressionChangedAfterItHasBeenCheckedError error issue
this.cdr.detectChagnes();
}