angular2 test, how to test event is bound to an element - unit-testing

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

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.

Angular2 testing directive with dynamically added DOM

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.

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.

Ember - triggerEvent helper

I'm having trouble triggering a "change" event in an ember acceptance test.
I have a rangeslider with an observed 'value' property. On change, the slider sends an api request which updates the url with params based on the slider value.
I am able to change the value of the slider in my test using jQuery, which successfully updates the slider in the UI...
$("#slider").val(3.0).change();
...but the change event isn't fired, so no api call. This same setup is working fine for similar tests where I am able to use "click" or "fillIn" to trigger a request.
I've tried using both the run loop and the triggerEvent helper (see below) but am having no luck.
// 1. using the run loop -- updates UI, but no change event fired
Ember.run(function() {
$("#gpa-slider").val(3.0).change();
});
andThen(() => {
assert.equal(currentURL(), "/athletes?gpa=3.0")
});
//2. using triggerEvent helper --
// doesn't update UI, slider value, or trigger change event
triggerEvent('#gpa-slider', 'change', {value: 3.8} )
andThen(() => {
assert.equal(currentURL(), "/athletes?gpa=3.0")
});
Am I setting up triggerEvent() incorrectly? Is there a better way to handle this?
I was able to get the change event to fire by clicking on the slider handle.
click(Ember.$('.rangeslider__handle'));
andThen(() => {
assert.equal(currentURL(), '/athletes?gpa=3.0');
});
I still don't know what I was doing wrong with the triggerEvent() helper, but maybe this will help some poor soul in the future.

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.