Angular2 testing directive with dynamically added DOM - unit-testing

I have an attribute directive that adds hidden input after host element and displays it when clicked on host element, it also hide it when input lose focus (blur event). It is basically used to edit values of some models without need to create forms/inputs for every single field.
Code: Plunker
I have a problem with 1 test case:
click on host element should clear inline display style
blur should set display style to none
it('should display input when clicked and hide on blur', () => {
i.click();
fixture.detectChanges();
expect(input.style.display).not.toBe('none');
input.blur();
fixture.detectChanges();
expect(input.style.display).toBe('none');
});
While running karma locally this test sometimes passes and sometimes fails, it fills like .blur() have some async behavior. I tried with fakeAsync and tick but with no luck. Maybe I am doing it in a completely wrong way.

Related

element is not rendering properly with *ngIf async pipe in unit test spec

What I'm trying to do is get the reference of a button in order to simulate a click event, my main problem is that the button is not rendering cause the component is not detecting the change of a list that should be populated by that time.
Here is my unit test code :
it('should add more cases when load more cases button is clicked', () => {
spyOn(component, 'loadMoreCases');
component.cases$.subscribe((cases) => {
fixture.detectChanges();
let buttons = fixture.debugElement.nativeElement.querySelectorAll('[nz-button]') as HTMLButtonElement[];
buttons[1].click();
expect(component.loadMoreCases).toBeCalled;
});
component.ngAfterViewInit();
fixture.detectChanges();
});
here is the *ngIf part in the HTML:
enter image description here
here is the service that is called after view init in the main component:
ngAfterViewInit(): void {
this.caseService
.getPaginatedCases(1, 15)
.pipe(take(1))
.subscribe((cases) => {
this.cases$.next(cases.list);
this.amountOfCases = cases.total;
});
}
I just want to know how to tell angular that the cases have some mock data already and inform the component to rerender in order to be able to access the button that it's in the *ngIf, I had been trying with everything, fakeAsync() using the tick function, async using done function, but nothing had worked.
How are you providing the data? Mocking the service and then returning them should be enough for it to work:
const getPaginatedCases = spyOn(caseService, "getPaginatedCasees");
getPaginatedCases.and.returnValue(of(mockCases));
Of course, it's important that this is run before ngOnInit() (you have the initializer in ngAfterViewInit(), but I think it belongs in init – doesn't matter for the sake of this answer anyway) is called, so fixture.detectChanges() can only be called after this has run. Don't forget that you might have a fixture.detectChanges() in the beforeEach() that is messing you up.

How can i simulate browser focus when testing using angular and jasmine?

I am trying to write a unit test that checks whether or not the effect of a focus event takes place. My actual test case is more complicated, but I have created a minimal reproduction with the following code:
it('testing input focus', async(() => {
let showDiv = false;
const template = `<div *ngIf="shouldShow" class='hidden-div'>
SHOW ME WHAT YOU GOT
</div>
<input (focus)="shouldShow = !shouldShow" name="input">`;
buildTestComponent(template, {shouldShow: showDiv}).then((fixture) => {
fixture.detectChanges();
const inputEl: HTMLInputElement = fixture.nativeElement.querySelector('input');
expect(fixture.nativeElement.querySelector('.hidden-div')).toBe(null);
inputEl.focus();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.hidden-div')).not.toBe(null);
});
}));
When I run this test with karma the test passes as long as I have focus on the chrome tab that is running the karma target. However, if the browser does not have focus the test fails (even if the browser is visible, but I click on another window) with error message:
Expected null not to be null.
I assume that when the Chrome tab doesn't have focus, the inputEl.focus() call doesn't actually get called but I don't know how to fix it. All other unit tests I have written pass regardless of browser focus. Has anyone run into this or have any ideas?
To trigger an event on an Angular element, you can use the built-in JavaScript ES6 method dispatchEvent with a subsequent call of Angular's change detection mechanism to get your DOM updated:
inputElement.dispatchEvent(new Event('focus'));
fixture.detectChanges();
A more elegant way to achieve the same thing is to use angular's wrapper method:
import { dispatchEvent } from '#angular/platform-browser/testing/src/browser_util'
dispatchEvent(inputElement, 'focus');
fixture.detectChanges();
An interesting one is when you want to set a value to your input element. You will need to first assign a string to the input's value property and then trigger an 'input' change event:
inputElement.value = 'abcd';
dispatchEvent(inputElement, 'input');
fixture.detectChanges();
Note: There are events that do not act the way you may expect. For example, dispatching a 'click' event will NOT give the focus to your input element! A workaround could be to first trigger a 'focus' event and then a 'click' event as follows:
dispatchEvent(inputElement, 'focus');
dispatchEvent(inputElement, 'input');
fixture.detectChanges();
All the available JavaScript events are here.

Ionic2 LoadingController spinner animation not working

I am trying to show Loading animation using Ionic2 for long service progress report:
this.loading = this.loadingCtrl.create({
content: 'Please wait...',
spinner: 'ripple' // <<------ Is that correct?
});
this.loading.present();
The result is a text box without any spinner.
This is 09/22/2016 Ionic2 using latest beta (11) and I cannot actually find any example like above anywhere. Could this be a future feature documented but not yet implemented?
I am talking about the Ionic2 LoadingController documentations here
Is ripple a custom spinner? Otherwise, you can check the by default available spinners here:
ios
ios-small
bubbles
circles
crescent
dots
The spinner name should be passed in the spinner property, and any
optional HTML can be passed in the content property. If you do not
pass a value to spinner the loading indicator will use the spinner
specified by the mode. To set the spinner name across the app, set the
value of loadingSpinner in your app's config. To hide the spinner, set
loadingSpinner: 'hide' in the app's config or pass spinner: 'hide' in
the loading options
So another option, would be to just use the specified spynner according to the mode like this:
this.loading = this.loadingCtrl.create({
content: 'Please wait...'
});
this.loading.present();
Thing is it might be that the Ionic team just put a way of extending the Loading control using a custom spinner, meaning that you have to put a SVG file name to the property.
It just looks obvious that you can put there one of the built-in spinner names.
Then again, it is reasonable to keep the app's design straight with the same spinner in all loaders. Guess that could be more bolded in the docs...

angular2 test, how to test event is bound to an element

I am writing an angular2 unit test for a component.
With fully using JQuery it's possible to find what event is bound to an element. However in Angular2, I am not sure it's possible or not
For example, the following code has a click event, which is a public function of a component
<button (click)="doLogin()" [disabled]="myDisabled">Login</button>
By reading DOM element, I can make it sure all properties and data binding is correct by running a test. Only thing that I don't know is, "event binding is correct or not" because the generated html is like the following
<button>Login</button>
I want to make it sure someone does not delete this event binding in the future by writing a test for it.
In summary, how do I know event is properly bound to DOM element?
EDIT:
Is there a way to know there is click event without actually clicking it?
You could use the approach below (calling the click:
it('should render list', injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
return tcb.createAsync(MyList).then((componentFixture: ComponentFixture) => {
const element = componentFixture.nativeElement;
componentFixture.detectChanges();
expect(element.querySelectorAll('li').length).toBe(5);
document.getElementById('test').click();
});
}));
See this question for more details:
How can I trigger a JavaScript event click

How to trigger scroll event in acceptance test

I need to trigger window scroll event to test infinite scrolling, I've tried to use triggerEvent, but it seems that I missing something and it doesn't work. I'm using Ember 2.0 and list is rendered inside the component if it matters. Test fails on last 2 assertions, scroll position doesn't change after triggering event
test 'loads more items when scrolling', (assert) ->
visit '/locations/1'
andThen ->
assert.equal(find('.items-list li').length, 30)
find(window).scrollTop(10000)
triggerEvent(window, 'scroll')
andThen ->
assert.ok(find(window).scrollTop() > 0, 'window should scroll')
assert.ok(find('.items-list li').length > 30, 'should load more items after reaching threshold')
Has anyone successfully triggered scroll event in their tests?
Finally I could make it work! Used #ember-testing-container instead window.
The code below was what worked for me:
andThen(() => {
Ember.$('#ember-testing-container').scrollTop(10000);
});
triggerEvent(Ember.$('#ember-testing-container'), 'scroll');
andThen(() => {
assert.ok(Ember.$('#ember-testing-container').scrollTop() > 0, 'window should scroll')
});
With ember-infinity you also need to scroll down the body before starting the test:
Ember.$('body').scrollTop(2000);
I have an possible answer for this.
Try
triggerEvent('.skip-button', 'scroll', [{isInTestEnvironment:true}] ).then...
Say .skip-button is a selector within your ember app, but it could be any other one.
The scroll event is detected by the ember app, as if it actually doesn't scroll anything... this is why I pass a isInTestEnvironment:true param to indicate the ember app I simulated a user scroll.
Not an ideal solution, but far better than no test at all.