Hello, I try to test a template driven form in Ionic v4.
But I can`t find a way to get the input element in the ion-input element.
This is what I tried:
login-register.page:
import { GroupsService } from './../../services/groups.service';
import { AuthenticateService, RegisterLoginReturnMessage } from './../../services/authenticate.service';
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { ActivatedRoute, Router, NavigationExtras } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { AlertController, ToastController } from '@ionic/angular';
import { TranslateService } from '@ngx-translate/core';
import { Location } from '@angular/common';
import { NavController } from '@ionic/angular';
import { addslashes } from './../../helpers';
import { FCM } from '@ionic-native/fcm/ngx';
import { StorageService } from 'src/app/services/storage.service';
import { NgForm } from '@angular/forms';
@Component({
selector: 'app-login-register',
templateUrl: './login-register.page.html',
styleUrls: ['./login-register.page.scss'],
})
export class LoginRegisterPage implements OnInit, OnDestroy {
@ViewChild('loginRegisterForm', { static: true })loginRegisterForm: NgForm;
navParams: any;
email: string;
userName = 'blafasel';
firstName: string;
lastName: string;
password: string;
passwordreset: string;
loading = false;
emailRegExpr = '[a-zA-Z]{1}[a-zA-Z0-9.-_]*@[a-zA-Z0-9]{1}[a-zA-Z0-9.-]*[a-zA-Z0-9]{1}[.][a-zA-Z]{2,}';
gdprChecked = false;
private unsubscribe: Subject<void> = new Subject();
constructor(
private route: ActivatedRoute,
private router: Router,
private authenticateService: AuthenticateService,
public alertController: AlertController,
public translateService: TranslateService,
private location: Location,
private navCtrl: NavController,
private groupsService: GroupsService,
private toastController: ToastController,
private fcm: FCM,
private storageService: StorageService
) {
this.route.queryParams.subscribe(params => {
if (this.router.getCurrentNavigation().extras.state) {
console.log('if (this.router.getCurrentNavigation().extras.state)');
this.navParams = this.router.getCurrentNavigation().extras.state;
console.log(this.navParams);
}
});
}
ngOnInit() {}
ionViewWillEnter() {
console.log('login is entered');
}
submitForm(): void {
this.loading = true;
console.log('submitform');
if (this.navParams.mode === 'register') {
this.authenticateService
.registerUserAndGetMail(this.userName, this.email, this.firstName, this.lastName)
.pipe(takeUntil(this.unsubscribe))
.subscribe(
(response: RegisterLoginReturnMessage) => {
this.loading = false;
if (response.valid === true) {
this.navCtrl.navigateRoot('');
this.presentAlert('success', 'registerSuccess');
} else {
this.presentAlert('error', response.message[0], response.message[1] ? response.message[1] : '');
}
console.log('response:', response);
},
(error: RegisterLoginReturnMessage) => {
this.loading = false;
this.presentAlert('error', error.message[0], error.message[1] ? error.message[1] : '');
console.log('error bla:', error);
}
);
}
if (this.navParams.mode === 'login') {
this.authenticateService
.getUserToken(this.userName, addslashes(this.password))
.then((response: RegisterLoginReturnMessage) => {
this.loading = false;
// this.location.back();
// this.presentAlert('success', response.message[0], response.message[1] ? response.message[1] : '');
this.presentToast('success', response.message[0], response.message[1] ? response.message[1] : '');
// Fetch Groups from backend
this.groupsService.fetchUserData().then(data => {
console.log('promise all data:', data);
this.fcm
.subscribeToTopic(data[2].id)
.then(() => {
console.log('succesfullysubscribe:', data[2].id);
})
.catch(err => {
console.error('error subscription:', err);
});
});
this.navCtrl.navigateRoot('');
console.log('response:', response);
})
.catch((error: RegisterLoginReturnMessage) => {
this.loading = false;
if (error.status === 0) {
this.presentAlert('error', 'wrongConnectionWp');
} else if (error.status === 403) {
this.presentAlert('error', 'wrongLoginData');
} else {
this.presentAlert('error', error.status.toString(), error.statusText);
}
console.log('error bla:', error);
});
}
if (this.navParams.mode === 'passwordReset') {
this.authenticateService
.resetPassword(this.passwordreset)
.then(response => {
this.loading = false;
this.presentAlert('success', response.message);
this.navCtrl.navigateRoot('');
})
.catch(error => {
this.loading = false;
this.presentAlert('error', error.message[0]);
console.log('error in reseet pass catch', error);
});
}
}
presentAlert(header: string, message: string, messageTwo?: string): void {
this.translateService.get([header, message, messageTwo ? messageTwo : '', 'OK']).subscribe(async (res: string[]) => {
const newAlert = await this.alertController.create({
header: res[header],
message: messageTwo ? res[message] + res[messageTwo] : res[message],
buttons: [
{
text: 'OK',
handler: () => {
console.log('ok pressed');
},
},
],
});
await newAlert.present();
});
}
async presentToast(title: string, message: string, message2: string = '', duration?: number): Promise<void> {
this.translateService.get([title, message, message2, 'OK']).subscribe(async (res: string[]) => {
const toast = await this.toastController.create({
header: res[title],
message: message2 ? `${res[message]}<br>${res[message2]}` : `${res[message]}`,
position: 'bottom',
duration: duration ? duration : 3000,
showCloseButton: true,
// buttons: [
// {
// side: 'end',
// // icon: 'star',
// text: 'OK',
// handler: () => {
// console.log('ok clicked');
// // const navigationExtras: NavigationExtras = {
// // state: {
// // landing_page: data.landing_page,
// // surveyUrl: data.surveyUrl,
// // },
// // };
// // console.log('Favorite clicked');
// // this.navCtrl.navigateForward(['/tabs/tab2'], navigationExtras);
// // // this.router.navigate([data.landing_page, data.surveyUrl]);
// },
// },
// // {
// // text: 'cancel',
// // role: 'cancel',
// // handler: () => {
// // console.log('Cancel clicked');
// // },
// // },
// ],
});
toast.present();
});
}
showPassword(passwordInput) {
passwordInput.type = passwordInput.type === 'password' ? 'text' : 'password';
}
gotToGdpr() {
const bla = this.translateService.currentLang;
console.log('gotogdpr clkicked:', this.translateService.currentLang);
const navigationExtras: NavigationExtras = {
state: {
postId:
this.translateService.currentLang === 'de' ||
this.translateService.currentLang === 'de-AT' ||
this.translateService.currentLang === 'de-CH' ||
this.translateService.currentLang === 'de-DE' ||
this.translateService.currentLang === 'de-LI'
? this.storageService.appData.gdprId.de
: this.storageService.appData.gdprId.en,
},
};
this.router.navigateByUrl('posts', navigationExtras);
}
ngOnDestroy(): void {
// For unsubscribing all Subscriptions
console.log('ngOnDestory');
this.unsubscribe.next();
this.unsubscribe.complete();
}
}
login-register.page.html:
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button text="{{ 'back' | translate }}"></ion-back-button>
</ion-buttons>
<ion-title *ngIf="navParams.mode === 'login'">{{ 'login' | translate }}</ion-title>
<ion-title *ngIf="navParams.mode === 'register'">{{ 'register' | translate }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<form #loginRegisterForm="ngForm" (ngSubmit)="submitForm()">
<ion-list inset>
<ion-item lines="inset" *ngIf="navParams.mode === 'register'">
<ion-input
[placeholder]="'email' | translate"
name="email"
id="emailField"
type="text"
required
[(ngModel)]="email"
[pattern]="emailRegExpr"
></ion-input>
</ion-item>
<ion-item lines="inset" *ngIf="navParams.mode === 'register'">
<ion-input
[placeholder]="'username' | translate"
name="userName"
id="userNameField"
type="text"
required
[(ngModel)]="userName"
pattern="[a-zA-Z0-9]{4,30}"
></ion-input>
</ion-item>
<div class="username-message" item-content *ngIf="navParams.mode === 'register'">
{{ 'usernameRestrictions' | translate }}
</div>
<ion-item lines="inset" *ngIf="navParams.mode === 'register'">
<ion-input
[placeholder]="'firstName' | translate"
name="firstName"
id="firstNameField"
type="text"
required
[(ngModel)]="firstName"
pattern="[a-zA-Z0-9\-\s]{1,100}"
></ion-input>
</ion-item>
<ion-item lines="inset" *ngIf="navParams.mode === 'register'">
<ion-input
[placeholder]="'lastName' | translate"
name="lastName"
id="userNameField"
type="text"
required
[(ngModel)]="lastName"
pattern="[a-zA-Z0-9\-\s]{1,100}"
></ion-input>
</ion-item>
<ion-item lines="inset" *ngIf="navParams.mode === 'login'">
<ion-input
[placeholder]="'username' | translate"
name="userName"
id="userNameField"
type="text"
required
[(ngModel)]="userName"
></ion-input>
</ion-item>
<ion-item lines="inset" *ngIf="navParams.mode === 'login'">
<ion-input
#passwordInput
[placeholder]="'password' | translate"
name="password"
id="passwordField"
type="password"
required
[(ngModel)]="password"
></ion-input>
<ion-icon
*ngIf="passwordInput.type === 'password'"
slot="end"
name="eye"
(click)="showPassword(passwordInput)"
style="font-size: 1.7rem;z-index:10"
></ion-icon>
<ion-icon
*ngIf="passwordInput.type == 'text'"
slot="end"
name="eye-off"
(click)="showPassword(passwordInput)"
style="font-size: 1.7rem;z-index:10"
></ion-icon>
</ion-item>
<ion-item lines="inset" *ngIf="navParams.mode === 'passwordReset'">
<ion-input name="passwordreset" id="passwordreset" type="text" required [(ngModel)]="passwordreset"></ion-input>
</ion-item>
<div class="username-message" item-content *ngIf="navParams.mode === 'passwordReset'">
{{ 'passwordReset' | translate }}
</div>
<ion-item lines="inset" *ngIf="navParams.mode === 'register'">
<a (click)="gotToGdpr()">{{ 'privacyPolicyLink' | translate }}</a>
</ion-item>
<ion-item lines="inset" *ngIf="navParams.mode === 'register'">
{{ 'gdprHint' | translate }}
<ion-checkbox pattern="true" name="gdprChecked" slot="end" [(ngModel)]="gdprChecked"></ion-checkbox>
</ion-item>
</ion-list>
<ion-row>
<ion-col>
<ion-button size="full" color="tertiary" type="submit" [disabled]="!loginRegisterForm.form.valid">{{
'submit' | translate
}}</ion-button>
</ion-col>
</ion-row>
</form>
<ion-spinner *ngIf="loading" class="spinner-large spinner-center" color="secondary"></ion-spinner>
</ion-content>
login-register.page.spec.ts:
import { AuthenticateService } from './../../services/authenticate.service';
import { IonicStorageModule } from '@ionic/storage';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
import { LoginRegisterPage } from './login-register.page';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { createTranslateLoader } from 'src/app/app.module';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { RouterTestingModule } from '@angular/router/testing';
import { FCM } from '@ionic-native/fcm/ngx';
import { FCMMock } from 'src/mocks/fcmMock';
import { Router } from '@angular/router';
import { By } from '@angular/platform-browser';
// test environment
const testModuleConfig = () => {
TestBed.configureTestingModule({
declarations: [LoginRegisterPage],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
providers: [{ provide: FCM, useClass: FCMMock }, AuthenticateService],
imports: [
FormsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: createTranslateLoader,
deps: [HttpClient],
},
}),
RouterTestingModule.withRoutes([]),
IonicModule.forRoot(),
IonicStorageModule.forRoot(),
HttpClientModule,
],
}).compileComponents();
};
describe('LoginRegisterPage', () => {
let component: LoginRegisterPage;
let fixture: ComponentFixture<LoginRegisterPage>;
let router: jasmine.SpyObj<Router>;
let service: AuthenticateService;
beforeEach(async(() => {
testModuleConfig();
}));
// beforeEach(() => {
// router = TestBed.get(Router);
// spyOn(router, 'getCurrentNavigation').and.returnValue({ extras: { state: { mode: 'login' } } });
// fixture = TestBed.createComponent(LoginRegisterPage);
// component = fixture.componentInstance;
// fixture.detectChanges();
// });
beforeEach(inject([AuthenticateService], (s) => {
service = s;
router = TestBed.get(Router);
spyOn(router, 'getCurrentNavigation').and.returnValue({ extras: { state: { mode: 'login' } } });
fixture = TestBed.createComponent(LoginRegisterPage);
component = fixture.componentInstance;
fixture.detectChanges();
}));
afterEach(() => {
fixture.destroy();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should be navparams login', () => {
expect(component.navParams).toEqual({ mode: 'login' });
});
it('form invalid when empty', () => {
console.log('component.loginRegisterForm.form', component.loginRegisterForm.form);
expect(component.loginRegisterForm.form.valid).toBeFalsy();
});
it('should contain the correct title', () => {
// component.title = 'Test';
fixture.detectChanges();
// const element = fixture.debugElement.nativeElement.querySelector('#userNameField');
const element = fixture.debugElement.query(By.css('#userNameField input'));
console.log('element+++++++++++++++++', element);
console.log('element+++++++++.textContent++++++++', element.textContent);
const inputs = fixture.debugElement.queryAll(By.css('input'));
console.log('first input element+++++++++++++++++', inputs[0]);
// expect(element.i).toEqual('blafasel');
});
it('test function should return bla', () => {
expect(service.testTestFunction()).toBe('bla');
});
});
The
console.log(‘element+++++++++++++++++’, element);
gives null. But the ion-input element is there with
fixture.debugElement.query(By.css(’#userNameField’));
Can somebody help me here to make the input element available in the tests?
Thanx a lot.