Error in Angular - javascript

I have this component in angular:
import { Component, AfterViewInit, ElementRef, ViewChild} from '#angular/core';
#Component({
selector: 'jhi-login-modal',
templateUrl: './login123.component.html'
})
export class JhiLoginModalComponent implements AfterViewInit {
a: number;
#ViewChild('hiddenLabel') hidden: ElementRef;
ngAfterViewInit() {
const a = 5;
console.log('Print a' + a);
console.log('Print htmlcontent' + this.hidden);
}
}
My HTML file is like this:
<label #hiddenLabel>Visible!</label>
And then I have a testing file for these where I want to test the html element. But firstly I want to make sure that I can print it and then test the value. Here is my test file:
import { ComponentFixture, TestBed, async, inject, fakeAsync, tick } from '#angular/core/testing';
import { JhiLoginModalComponent } from '../../../../../../main/webapp/app/shared/login/login.component';
describe('Component Tests', () => {
describe('LoginComponent', () => {
let comp: JhiLoginModalComponent;
let fixture: ComponentFixture<JhiLoginModalComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [JhiLoginModalComponent]
})
.overrideTemplate(JhiLoginModalComponent, '')
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(JhiLoginModalComponent);
comp = fixture.componentInstance;
});
it ('first test', async(() => {
console.log('holaaa');
fixture.detectChanges();
}));
});
});
What I get as a result in my console is that test passed. Normally this should be ok. And then it prints holaaa and then print a5 which is again alright. Then it prints Print htmlcontentundefined instead of printing Visible. What is the difference between teh number and teh HTMLElement?? Is there another way to reference the HTML elements?? As much as I have read on this it should be this way.
Thanks!

It seems that your hiddenLabel child is undefined, meaning it is not being picked up by your component typescript spec file. This is due to the .overrideTemplate(JhiLoginModalComponent, ''). This line replaces your JhiLoginModalComponent template with nothing. Thus, when your JhiLoginModalComponent trys to get the hiddenLabel viewChild it cannot find anything, therefore it is undefined.
You should change your spec to look like the following:
import { ComponentFixture, TestBed, async, inject, fakeAsync, tick } from '#angular/core/testing';
import { JhiLoginModalComponent } from '../../../../../../main/webapp/app/shared/login/login.component';
describe('Component Tests', () => {
describe('LoginComponent', () => {
let comp: JhiLoginModalComponent;
let fixture: ComponentFixture<JhiLoginModalComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [JhiLoginModalComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(JhiLoginModalComponent);
comp = fixture.componentInstance;
fixture.detectChanges();
});
it ('first test', async(() => {
console.log('holaaa');
fixture.detectChanges();
}));
});
});

Related

Cannot read properties of null (reading 'nativeElement') - Test Angular

I'm new to Angular and I'm trying to perform a test... I want to test my function in component.ts that receives an event through click, and this value passes to an output for the component dad.
The test code looks like this...
import { ComponentFixture, TestBed } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { QuadradosComponent } from './quadrados.component';
describe('QuadradosComponent', () => {
let component: QuadradosComponent;
let fixture: ComponentFixture<QuadradosComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ QuadradosComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(QuadradosComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('Deve emitir mensagem quando clicar na div', () => {
const emitMessageSpy = spyOn(component.eventoOutput, 'emit')
let divClick = fixture.debugElement.query(By.css('.casa')).nativeElement
divClick.click()
expect(emitMessageSpy).toHaveBeenCalled
})
});
The component.ts
import { Component, EventEmitter, Input, Output, OnInit} from '#angular/core';
import { JogadasService } from 'src/app/services/jogadas.service';
#Component({
selector: 'app-quadrados',
templateUrl: './quadrados.component.html',
styleUrls: ['./quadrados.component.css']
})
export class QuadradosComponent implements OnInit{
constructor(public service: JogadasService) {}
ngOnInit(): void {
this.service.emitirVitoria.subscribe(
array => this.vitoria.push(...array)
)
console.log(document.getElementsByClassName('casa'))
}
vitoria: number[] =[];
posicoes: number = 0;
#Input() quadrados?: Array<number>;
#Output() eventoOutput = new EventEmitter();
#Output() index = new EventEmitter();
eventoClick(evento: MouseEvent) {
this.eventoOutput.emit(evento);
console.log(document.getElementsByClassName('casa'))
}
pegaIndex(index: number) {
this.posicoes = index;
this.index.emit(this.posicoes);
}
vitoriaArray(modelo: number) {
for(let i = 0; i< this.vitoria.length; i++) {
if(this.vitoria[i] == modelo) {
return this.vitoria[i]
}
}
}
}
the component HTML:
<div class="centraliza">
<div class="jogo-da-velha">
<div class="linha">
<div class="casa" *ngFor="let quadrado of quadrados, let i = index"
(click)="pegaIndex(i)"
[ngClass]="i == vitoriaArray(i) ? 'casa-vencedor' : null"
(click)="eventoClick($event)" ></div>
</div>
</div>
</div>
I'm using Angular/Cli in version 15, I'm not getting a good understanding of the tests in Angular, thanks for the help.
div.casa is being rendered with an *ngFor and you have to make sure quadrados is a non empty array and not undefined.
Try this:
it('Deve emitir mensagem quando clicar na div', () => {
const emitMessageSpy = spyOn(component.eventoOutput, 'emit')
// Mock quadrados here
component.quadrados = [{ } as any];
// detect the changes here since the view model changed
fixture.detectChanges();
// the div should be there now
let divClick = fixture.debugElement.query(By.css('.casa')).nativeElement
divClick.click()
expect(emitMessageSpy).toHaveBeenCalled
})
Here is a good resource on learning testing with Angular: https://testing-angular.com/.

Video tag with controls attribute causing unit tests to fail

I am building a webapp with Angular 7.2.4. I am using the default unit testing library that the angular CLI (7.3.1) comes with.
I keep running into this error as a failure when the unit tests run:
An error was thrown in afterAll\nResizeObserver loop limit exceeded thrown
It seems that the controls attribute on one of the video inputs is causing this. If I remove the controls attribute all tests pass. I have no idea how to get around this as I need to use the video controls.
Can someone please point me in the right direction on how to resolve this without building a custom video player?
For reference here is the component (which is literally just a wrapper around the video player)
video.component.html
<video preload="auto"
controls
#videoPlayer
controlsList="nodownload"
(ended)="videoEnded($event)">
<source [src]="getVideoSource()" type="video/mp4"/>
</video>
video.component.ts
import {Component, ElementRef, Input, OnChanges, OnInit, ViewChild} from '#angular/core';
import {ConfigService} from '../config.service';
#Component({
selector: 'app-video',
templateUrl: './video.component.html',
styleUrls: ['./video.component.scss']
})
export class VideoComponent implements OnInit, OnChanges {
#ViewChild('videoPlayer') videoPlayer: ElementRef;
#Input() src: string;
#Input() ended: (event: Event) => void;
constructor(private cs: ConfigService) {
}
ngOnInit() {
this.videoPlayer.nativeElement.src = this.getVideoSource();
}
ngOnChanges() {
this.videoPlayer.nativeElement.src = this.getVideoSource();
}
getVideoSource(): string {
return this.cs.getConfig('videosUrl') + this.src;
}
videoEnded($event) {
if (this.ended) {
this.ended($event);
}
}
}
video.component.spec.ts
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { VideoComponent } from './video.component';
describe('VideoComponent', () => {
let component: VideoComponent;
let fixture: ComponentFixture<VideoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ VideoComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(VideoComponent);
component = fixture.componentInstance;
component.src = '';
component.ended = () => {};
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
afterEach(() => {
fixture.destroy();
});
});
UPDATE: It would appear that this is a Chrome issue. This does not happen in firefox or safari
I had the exact same issue. I also created a wrapper component over video tag and my tests were failing with the same error, and the issue was also the 'controls' attribute.
I couldn't find the cause of it though.
I implemented a workaround for my tests where I overwritten the controller's template with TestBed.overrideComponent()
In you case it would be as follows:
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { VideoComponent } from './video.component';
describe('VideoComponent', () => {
let component: VideoComponent;
let fixture: ComponentFixture<VideoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ VideoComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.overrideComponent(VideoComponent, {
set: {
template: `
<video
preload="auto"
#videoPlayer
(ended)="videoEnded($event)">
<source [src]="getVideoSource()" type="video/mp4"/>
</video>`
}
}).createComponent(VideoComponent);
component = fixture.componentInstance;
component.src = '';
component.ended = () => {};
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
afterEach(() => {
fixture.destroy();
});
});
Notice that there is no 'controls' attribute in the overwritten template.
It should be enough to unit tests the logic of your component.
We had this issue today with our CI's Chrome version, while it worked perfectly on dev computers.
Our solution: update Chrome on our CI (in this case, from 75 to 91).

ngOnInit and MediaChange testing with Karma

I am trying to test the code in the ngOnInit method. The code watches for change in screen size for a navigation bar to resize down to mobile or to stay as a top bar. I have tried a for about a week and keep getting a slew of different errors when I test. I have left out some code for comp.component.ts as the other code is not necessary for this. I keep getting subscribe is not a method or Can't resolve all parameters for MediaChange: (?, ?, ?, ?). Any advice on how I can achieve writing a test for this or any resources you might suggest looking at to help me figure this out.
comp.component.ts
import { Component, OnInit } from '#angular/core';
import { Subscription } from 'rxjs';
import { MediaChange, ObservableMedia } from '#angular/flex-layout';
#Component({
selector: 'app-comp',
templateUrl: './comp.component.html',
styleUrls: ['./comp.component.scss']
})
export class NavigationComponent implements OnInit {
isOpen: Boolean;
watcher: Subscription;
activeMediaQuery = "";
media: ObservableMedia;
constructor() {
this.isOpen = false;
}
ngOnInit(): void {
this.watcher = this.media.subscribe((change: MediaChange) => {
this.activeMediaQuery = change ? `'${change.mqAlias}' = (${change.mediaQuery})` : '';
this.isOpen = false;
});
}
navPressed(event, path): void {
this.navClick.emit(path);
if ( this.checkSize() ) this.toggle();
}
checkSize(): Boolean {
return this.activeMediaQuery.includes('xs') || this.activeMediaQuery.includes('sm');
}
}
comp.component.spec.ts
import { Component } from '#angular/core';
import { ComponentFixture, TestBed } from '#angular/core/testing';
import { DebugElement } from '#angular/core';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { MatButtonModule, MatToolbarModule, MatIconModule } from '#angular/material';
import { CompComponent } from './comp.component';
import { Subscription } from 'rxjs';
import { MediaChange, ObservableMedia } from '#angular/flex-layout';
#Component({
selector: 'app-test-component-wrapper',
template: '<app-navigation [navItems]="clickables" (navClick)="handleNavClick($event)"></app-navigation>'
})
class TestWrapperComponent {
clickables = [
{ path: '/login', label: 'Login', onClick() {} }
];
}
describe('app testing', () => {
let component: CompComponent;
let fixture: ComponentFixture<TestWrapperComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
MatButtonModule,
MatToolbarModule,
MatIconModule,
BrowserAnimationsModule
],
declarations: [
TestWrapperComponent,
NavigationComponent
],
providers: [
ObservableMedia,
MediaChange,
Subscription
]
}).compileComponents();
fixture = TestBed.createComponent(TestWrapperComponent);
}));
it('should create and have Login label', () => {
// EDIT START
spyOn(ObservableMedia, 'prototype');
// EDIT END
expect(fixture).toBeTruthy();
fixture.detectChanges();
fixture.whenStable().then(() => {
component = fixture.debugElement.children[0].componentInstance;
expect(component.navItems[0].label).toBe('Login');
});
});
});
EDIT: Added the 'EDIT' comment in the code with the code I have added. I am now getting the resolve all parameters for MediaChange: (?, ?, ?, ?) error which I think is forward progress from the subscribe error mentioned above.
Some observations:
ObservableMedia from flex-layout needs to be injected into your component to work. Details here
You aren't providing MediaChange or Subscription in the original component, so no need to in the TestBed either.
In the stackblitz below I had to make a few assumptions. Let me know if any of these are wrong, or just go ahead and update the stackblitz:
In your spec you imported CompComponent, but in comp.component.ts you defined NavigationComponent. Of the two I chose to use NavigationComponent.
navClick was missing from your code above, so I assumed it is an #Output from your component (since you emit a path to it).
navItems was also missing from the code above, but since you are testing it I assumed it was important and guessed it is an input to your component (again, just by the way you were using it).
You didn't include your template, so I mocked it very simply.
toggle was called from within navPressed, but didn't exist so I created it as an empty function.
Here is the stackblitz: https://stackblitz.com/edit/stackoverflow-q-53024049?file=app%2Fmy.component.spec.ts
To fix what you had: I made the changes above and mocked the ObservableMedia object passed in with the following:
let mockFlex = jasmine.createSpyObj({
subscribe: ({mqAlias: 'xs', mediaQuery: ''}),
isActive: true,
});
I also changed the providers array to the following:
providers: [
{ provide: ObservableMedia, useValue: mockFlex }
]
Check the stackblitz for all the details. As you can see there, the test now passes.

Binding input value in unit test

I have a simple Angular component with text input which uses ngModel binding. the component works fine, but not the unit-test.
helloworld.component.html:
<div>
<input [(ngModel)]="name" >
<p>{{msg}} {{name}}</p>
</div>
helloworld.component.ts:
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'hello-world',
templateUrl: './helloworld.component.html',
styleUrls: ['./helloworld.component.css']
})
export class HelloWorldComponent {
#Input() name: string = "World";
#Input() msg: string = "Hello";
}
helloworld.component.spec.ts:
import { async, ComponentFixture, TestBed, fakeAsync, tick } from
'#angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '#angular/core';
import { HelloWorldComponent } from './helloworld.component';
describe('HelloWorldComponent', () => {
let component: HelloWorldComponent;
let fixture: ComponentFixture<HelloWorldComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HelloWorldComponent ],
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HelloWorldComponent);
component = fixture.componentInstance;
});
it('should print the name on input', ()=>{
component.msg = "Hi";
fixture.detectChanges();
var nameInput: HTMLInputElement = fixture.nativeElement.querySelector('input');
var label: HTMLElement = fixture.nativeElement.querySelector('p');
nameInput.value = 'John';
nameInput.dispatchEvent(new Event('input'));
fixture.detectChanges();
expect(component.msg).toBe('Hi');
expect(component.name).toBe('John');
expect(label.textContent).toBe('Hi John');
});
});
The test fails because name is not set to 'John' but still has the default value 'World'. The provided code is in my eyes equivalent to the example in Angular documentation (https://angular.io/guide/testing#component-binding)
There are some articles that explain to perform the test in async() and use fixture.whenStable().then() after changing the input value.
I also found the hint to run the test inside fakeAsync() and use tick() to wait for the input value to be bound to name. Both didn't help.
What am I missing?

Unit test error: Cannot call Promise.then from within a sync test

I started looking into unit testing angular 2 applications, but I'm stuck even in the simplest examples. I just want to run a simple test to see if it even works, basically what I want is to compare a value from the title page to the one in the test.
This is the error I'm getting, but I don't see where the error is coming from since everything looks to be synchronous to me.
Error: Error: Cannot call Promise.then from within a sync test.
Unit test:
import { ComponentFixture, TestBed } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { DebugElement, Input} from '#angular/core';
import { ToDoComponent } from './todo.component';
import { FormsModule } from '#angular/forms';
describe(("test input "),() => {
let comp: ToDoComponent;
let fixture: ComponentFixture<ToDoComponent>;
let de: DebugElement;
let el: HTMLElement;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ ToDoComponent ],
imports: [ FormsModule ]
})
.compileComponents();
});
fixture = TestBed.createComponent(ToDoComponent);
comp = fixture.componentInstance;
de = fixture.debugElement.query(By.css("h1"));
el = de.nativeElement;
it('should display a different test title', () => {
comp.pageTitle = 'Test Title';
fixture.detectChanges();
expect(el.textContent).toBe('Test Title423');
});
});
My component:
import {Component} from "#angular/core";
import {Note} from "app/note";
#Component({
selector : "toDoArea",
templateUrl : "todo.component.html"
})
export class ToDoComponent{
pageTitle : string = "Test";
noteText : string ="";
noteArray : Note[] = [];
counter : number = 1;
removeCount : number = 1;
addNote() : void {
if (this.noteText.length > 0){
var a = this.noteText;
var n1 : Note = new Note();
n1.noteText = a;
n1.noteId = this.counter;
this.counter = this.counter + 1;
this.noteText = "";
this.noteArray.push(n1);
}
}
removeNote(selectedNote : Note) :void{
this.noteArray.splice(this.noteArray.indexOf(selectedNote),this.removeCount);
}
}
Move your variable initialization inside a beforeEach.
You shouldn't be getting things out of the TestBed or managing the fixture or component in the describe scope. You should only do these things within the scope of a test run: inside a beforeEach/beforeAll, afterEach/afterAll, or inside an it.
describe(("test input "), () => {
let comp: ToDoComponent;
let fixture: ComponentFixture<ToDoComponent>;
let de: DebugElement;
let el: HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ToDoComponent],
imports: [FormsModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ToDoComponent);
comp = fixture.componentInstance;
de = fixture.debugElement.query(By.css("h1"));
el = de.nativeElement;
})
it('should display a different test title', () => {
comp.pageTitle = 'Test Title';
fixture.detectChanges();
expect(el.textContent).toBe('Test Title423');
});
});
See also
https://angular.io/docs/ts/latest/guide/testing.html#!#waiting-compile-components
I got the same error for a different reason. I put a TestBed.get(Dependency) call within a describe block. The fix was moving it to the it block.
Wrong:
describe('someFunction', () => {
const dependency = TestBed.get(Dependency); // this was causing the error
it('should not fail', () => {
someFunction(dependency);
});
});
Fixed:
describe('someFunction', () => {
it('should not fail', () => {
const dependency = TestBed.get(Dependency); // putting it here fixed the issue
someFunction(dependency);
});
});

Categories