I have a button component that accepts a promise and disables the button until the promise has resolved and I want to write a unit test for this functionality.
My button component has an input for the promise
/**
* A single promise that triggers the busy state until it resolves
*/
@Input()
public promise: Promise<any>;
Inside ngOnChanges I listen for the promise
/**
* Enable button busy state until promise resolves
*/
if (changes && changes.promise && changes.promise.currentValue) {
this.busyUntilPromiseResolves(changes.promise.currentValue);
}
Then I store an array of active promises so multiple promises can be passed in
/**
* Add a promise that will keep the button in a busy state until it resolves
* @param activityProimise
*/
private busyUntilPromiseResolves(activityProimise) {
this.activityPromises.push(activityProimise);
activityProimise.then(() => {
this.activityPromises.splice(this.activityPromises.indexOf(activityProimise), 1);
});
}
Then finally in my template I disable the button if there are any promises in the array.
[disabled]="activityPromises.length > 0"
I have been dabbling and trying different things to make this work, this is my current test code that doesn't work. Basically I need to check that the button is disabled before the promise resolves, and I will then want to do another test that checks it is re-enabled after the promise resolves.
it('should be disabled when passed a single promise', async((done) => {
let promise;
return promise = new Promise(resolve => {
component.promise = promise;
fixture.detectChanges();
expect(buttonElement.disabled).toBe(true);
return resolve();
});
}));
As always any help will be appreciated, thanks.
This means that when we call detectChanges Angular will not detect any changes and not call ngOnChanges. So, how can change our test so ngOnChanges is called? wrap Greeter in a host component on which we can change the name property.
Fixtures have access to a debugElement , which will give you access to the internals of the component fixture. Change detection isn't done automatically, so you'll call detectChanges on a fixture to tell Angular to run change detection.
ngOnChanges gets called before ngOnInit and whenever a component's bound input is changed FROM THE PARENT COMPONENT. Remember that ngOnChanges is specific to bound inputs on the component. This means if you don't have any @Input properties on a child, ngOnChanges will never get called.
detectChanges() tells Angular to run change-detection. Finally! Every time it is called, it updates data bindings like ng-if, and re-renders the component based on the updated data. Calling this function will cause ngOnInit to run only the first time it is called.
The short answer to this question is that ngOnChanges doesn't get fired automatically during unit tests so I had to call it manually.
it('should be disabled when passed a single promise', async () => {
let promise;
let resolve;
// should not be disabled here
expect(buttonElement.disabled).toBe(false);
// Pass a promise to the component to disable it
promise = new Promise((r) => (resolve = r));
component.ngOnChanges({
promise: new SimpleChange(null, promise, null),
});
fixture.detectChanges();
expect(buttonElement.disabled).toBe(true);
// now resolve the promise and make sure it's enabled again.
promise.then(() => {
fixture.detectChanges();
expect(buttonElement.disabled).toBe(false);
});
resolve();
});
Have you tried asserting before adding that promise?
it('should be disabled when passed a single promise', async(() => {
let promise;
// should not be disabledhere
expect(buttonElement.disabled).toBe(false);
let resolve;
const promise = new Promise(r => resolve = r);
component.promise = promise;
component.ngOnChanges(); // Call this here, TestBed doesn't know it needs to.
fixture.detectChanges();
expect(buttonElement.disabled).toBe(true);
// now resolve the promise and make sure it's disabled again.
resolve();
fixture.detectChanges();
expect(buttonElement.disabled).toBe(false);
}));
Edit: As noted in the comments, the way you set that promise
property doesn't work with TestBed, so it never calls your ngOnChanges() method. You could call it directly, like I did above.
Alternatively, wrap your test component in a host component:
@Component({
selector: 'test-component',
template: `<my-component [promise]="promise"></my-component>`
})
class TestComponent {
promise;
}
// then use that in your test instead:
const wrapperCmp = TestBed.createComponent(TestComponent);
wrapperCmp.promise = promise; // and check for the button state before and after all the changes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With