Unit testing - Getting issue with Alert Controller in Ionic Framework - ionic-framework

I am trying to write a testcase for a method which has been called or not. Inside that method, I am calling an alert confirmation box.
I am getting an error like
Failed: this.alertCtrl.create is not a function
Component.ts
submitTicket(comments) {
if (comments.length > 0) {
const prompt = this.alertCtrl.create({
title: "<span> Improve Solution </span>",
message: "<span>" + 'Are you sure you want <br>' + "</span>" +
"<span>" + 'to submit this improvement' + "</span>",
enableBackdropDismiss: false,
buttons: [
{
text: 'Cancel',
handler: data => {
// Some stuff
}
},
{
text: 'Improve Solution',
handler: data => {
//Some stuff
}
}
]
});
prompt.present();
} else {
this.errorMsg = true;
}
}
component.spec.ts
import {AlertControllerMock } from 'ionic-mocks';
beforeEach(async(()=> {
TestBed.configureTestingModule({
declarations: [ImprovedsolutionsPage],
imports: [
IonicModule.forRoot(ImprovedsolutionsPage),
HttpClientTestingModule
],
providers: [
NavController,
AppService,
AlertController,
ImprovedsolutionsPage,
{provide: ViewController, useClass: ViewControllerMock},
{provide: LoadingController, useClass: LoadingControllerMock},
{provide: AlertController, useClass: AlertControllerMock},
]
}).compileComponents
}))
beforeEach(()=> {
fixture=TestBed.createComponent(ImprovedsolutionsPage)
component=fixture.componentInstance
fixture.detectChanges()
})
it('should be call submitTicket method', async(()= > {
spyOn(component, 'submitTicket').and.callThrough()
let comment='Needs to improve in detailing '
component.submitTicket(comment)
expect(component.submitTicket).toHaveBeenCalled()
}))
Here I am using ionic-mocks module and I imported AlertControllerMock as shown in the above code. And I am using ionic version 3. For testing I am using Karma and jasmine
Could someone please help me out in this issue.

[Updated] Here is my tip: I am not able to check your mock's implementation. However, make sure you have a separate boolean var to testify if the mocked function, for example, create, is called elsewhere.
create(opts?: AlertOptions): Promise<HTMLIonAlertElement> {
this.createAlertCalled = true;
this.opts = opts;
const self = this;
return Promise.resolve(<HTMLIonAlertElement>{
present: (): Promise<void> => {
self.presentCalled = true;
return Promise.resolve();
}
});
}
Subsequently, do a spyOn with AlertControllerMock on the mentioned variable and expect the assertion to be truthy.

Related

ngrx jasmine-marbles test resulting the "Received" part returning '?' question mark

I am having an issue with the following tech-stack:
Angular v8,
ionic Angular v5,
ngrx v8,
jasmine-marbles v0.8.3.
I am writing a unit test for "ngrx", in particular the "effects" part.
Following is my code snippet:
import { TestBed } from '#angular/core/testing';
import { provideMockActions } from '#ngrx/effects/testing';
import { Observable } from 'rxjs';
import { InformationEffects } from './information.effects';
import { HttpClientTestingModule, HttpTestingController } from '#angular/common/http/testing';
import { Storage } from '#ionic/storage';
import { DataService } from 'src/app/shared/services/data.service';
import { RouterTestingModule } from '#angular/router/testing';
import { cold, hot } from 'jasmine-marbles';
import { MockStore, provideMockStore } from '#ngrx/store/testing';
import {
InformationRequested,
InformationSuccess,
} from './information.actions';
describe('Information Effects', () => {
let information = {} as any;
const initialState = { information: information};
let actions$: Observable<any>;
let effects: InformationEffects;
let store: MockStore<any>;
let dataService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, RouterTestingModule],
providers: [
{
provide: Storage,
},
InformationEffects,
MockStore,
provideMockStore({ initialState }),
provideMockActions(() => actions$),
{
provide: DataService,
useValue: jasmine.createSpyObj('DataService', ['getInformation'])
}
]
});
effects = TestBed.get(InformationEffects);
store = TestBed.get(MockStore);
dataService = TestBed.get(DataService);
});
it('should be created', () => {
expect(effects).toBeTruthy();
});
describe('INFORMATION_REQUESTED', () => {
it('should return an InformationSucess action, with the user, on success', () => {
let language = {} as any;
const action = new InformationRequested(language);
const outcome = new InformationSuccess(information);
actions$ = hot('-a-', { a: action });
const response = cold('-a|', { a: information });
const expected = cold('--b', { b: outcome });
dataService.getInformation.and.returnValue(response);
expect(effects.informationAction$).toBeObservable(expected);
});
});
});
When I run the test using "npm test", it failed at the "Received" part showing "?".
It says:
Expected: --b,
Received: --?,
Expected:
[{"frame":20,"notification":{"kind":"N","value":{"payload":{},"type":"[Information]
INFORMATION Success"},"hasValue":true}}]
Received:
[{"frame":20,"notification":{"kind":"N","value":{"payload":{},"type":"[Information]
INFORMATION Success"},"hasValue":true}}],
Please refer below figure:
enter image description here
I had searched the internet for this question mark in the "Received:" section, to no avail. I had also researched on each jasmine-marbles syntaxes, as well the (hot & cold) observable, to understand why & how to use it. Still no solution to get rid of the '?' question mark to have the unit-test being "Success". As such please help me.

Can't bind to 'stackConfig' since it isn't a known property of 'div'. ("

I'm trying to build a tinder cards app in ionic2 following this tutorial https://devdactic.com/ionic-2-tinder-cards but got an error:
Unhandled Promise rejection: Template parse errors:
Can't bind to 'stackConfig' since it isn't a known property of 'div'. ("
Any solution?
add these two modules HttpModule,SwingModule to your pagename.module.ts because the tutorial's using ionic 2 I think. We'll have to individually add the swing module to the new page created.
You need to add the SwingModule import in the app.module.ts. See the sample app config at https://github.com/ksachdeva/angular2-swing-example/blob/master/src/app/app.module.tsenter code here
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
SwingModule
],
providers: [],
bootstrap: [AppComponent]
})
cards: Array<any>;
stackConfig: any;
recentCard: string = '';
constructor(public navCtrl: NavController,private http: Http, public navParams: NavParams,public modalCtrl: ModalController) {
this.stackConfig = {
throwOutConfidence: (offsetX, offsetY, element) => {
return Math.min(Math.abs(offsetX) / (element.offsetWidth/2), 1);
},
transform: (element, x, y, r) => {
this.onItemMove(element, x, y, r);
},
throwOutDistance: (d) => {
return 800;
}
};
}
ngAfterViewInit() {
// Either subscribe in controller or set in HTML
this.swingStack.throwin.subscribe((event: DragEvent) => {
event.target.style.background = '#ffffff';
});
this.cards = [{email: ''}];
this.addNewCards(1);
}

Prevent multiple Ionic Alerts from stacking up

How can I detect if ionic 2 alert ui component instance is already open in order not to present another alert ?
I ended up writing a wrapping provider for Ionic's Alert controller like so :
import { Injectable } from '#angular/core';
import { AlertController } from 'ionic-angular';
#Injectable()
export class Alert {
public alertPresented: any;
constructor(public alertCtrl: AlertController) {
this.alertPresented = false
}
present(title, subTitle) {
let vm = this
if(!vm.alertPresented) {
vm.alertPresented = true
vm.alertCtrl.create({
title: title,
subTitle: subTitle,
buttons: [{
text: 'OK',
handler: () => {
vm.alertPresented = false
}
}],
}).present();
}
}
}
where alertPresented flag prevents more than one instance from being presented
I have another idea that you can assign message for a variable and check new message is equal to it or not. If equal, return.
This is my code and hope you enjoy with it.
import { Injectable } from '#angular/core';
import { AlertController } from 'ionic-angular';
#Injectable()
export class AlertProvider {
public showingMessage = ""
constructor(
private alertController: AlertController
) {}
showAlert(message) {
// Check this message is showing or not
if (message === this.showingMessage) {
return
}
this.showingMessage = message
this.alertController.create({
title: "APP_NAME",
message: message,
buttons: [{
text: "OK",
handler: () => {
this.showingMessage = ""
}
}]
}).present()
}
}
You can create an AlertService to handle that with more options without inject an event for the buttons
import { Injectable } from '#angular/core';
import { AlertController, Alert } from 'ionic-angular';
/**
* A simple alert class to show only one alert at the same time
*/
#Injectable()
export class AlertService {
currentAlert: Alert
constructor(private alertCtrl: AlertController) {
}
show(title, message, buttons: any = [], inputs: any = [], cssClass = '') {
if (!buttons.length) {
buttons.push('Ok')
}
let alertOptions: any = {
title: title,
subTitle: message,
buttons: buttons,
cssClass: buttons.length === 2 ? 'confirmAlert' : cssClass
}
if (inputs.length) {
alertOptions.inputs = inputs
}
if (!this.currentAlert) {
this.currentAlert = this.alertCtrl.create(alertOptions)
this.currentAlert.present()
this.currentAlert.onDidDismiss(() => {
this.currentAlert = null
})
}
return this.currentAlert
}
}
Regards, Nicholls
My solution worked, i had to put a boolean and set it true after a cancel event and set it false when an alert is presented
if (this.network_alert) {
let alert = await this.alertController.create({
header: "No Network",
message:
"Please check your internet connection",
buttons: [{
text: "Dismiss",
role: 'cancel',
handler: () => {
console.log('Cancel clicked');
this.network_alert = true
}
}],
});
await alert.present();
this.network_alert = false
}
}

Ionic2 alert with dropdown?

I am building an Ionic2 app. I have an alert like following:
constructor(private platform: Platform, public nav : NavController,
public exhibitionSurveyObjectService : ExhibitionSurveyObjectService ) {
this.initializeMap();
this.nav=nav;
this.testArray=[];
this.area=null;
}
addSurveyObject(){
let prompt = Alert.create({
title: 'Subscribe to our service',
message: "All the fields are necessary",
inputs: [
{
name: 'name',
placeholder: 'Name'
},
....
{
name: 'cycle',
placeholder: 'Cycle: once/weekly/monthly'
},
{
name: 'object_type',
placeholder: 'Farm/Solarpanel/plain'
},
],
buttons: [
....
{
text: 'Save',
handler: data => {
this.createExhibitionSuveyObject(data);
}
}
]
});
this.nav.present(prompt);
}
createExhibitionSuveyObject(data: any){
var cycle = data.cycle;
cycle = cycle.toUpperCase()
console.log(cycle)
var type = data.object_type;
type = type.toUpperCase()
console.log(type)
this.exhibitionSurveyObjectService.addObject(
data.name, data.farmer_email,
data.farmer_name, data.size, data.path, cycle, type).subscribe(
response => {
this.exhibitionSurveyObjects = response;
this.sayThanks();
},
error => {
this.errorMessage = <any>error;
console.log("error")
}
);
}
sayThanks(){
let alert = Alert.create({
title: 'Thank you!',
subTitle: 'We have received your data, we will get back to you soon!',
buttons: [{
text: 'Ok',
handler: () => {
this.nav.push(HomePage)
}
}]
});
this.nav.present(alert);
}
I want the last two fields to be dropdowns. How can I achieve this?
UPDATE: updated the code snippet with some more code. How it can be updated to use Modal instead of alert?
Just like you can see in Ionic2 docs
Alerts can also include several different inputs whose data can be
passed back to the app. Inputs can be used as a simple way to prompt
users for information. Radios, checkboxes and text inputs are all
accepted, but they cannot be mixed. For example, an alert could have
all radio button inputs, or all checkbox inputs, but the same alert
cannot mix radio and checkbox inputs.
And...
If you require a complex form UI which doesn't fit within the
guidelines of an alert then we recommend building the form within a
modal instead.
So you'll have to create a new Component with that form and then use it to create the Modal:
import { Modal, NavController, NavParams } from 'ionic-angular';
#Component(...)
class YourPage {
constructor(nav: NavController) {
this.nav = nav;
}
presentSubscriptionModal() {
let subscriptionModal = Modal.create(Subscription, { yourParam: paramValue });
this.nav.present(subscriptionModal);
}
}
#Component(...)
class Subscription{
constructor(params: NavParams) {
let param = params.get('yourParam');
}
}

Ionic 2 HTTP request not working - Angular 2

Hi I'm trying to do a simple Http GET request, but can't get it to work in ionic v2 Beta...
here is my app.js:
import {App, Platform} from 'ionic-angular';
import {TabsPage} from './pages/tabs/tabs';
import {HTTP_BINDINGS} from 'angular2/http';
#App({
template: '<ion-nav [root]="rootPage"></ion-nav>',
providers: [HTTP_BINDINGS],
config: {} // http://ionicframework.com/docs/v2/api/config/Config/
})
export class MyApp {
static get parameters() {
return [[Platform]];
}
constructor(platform) {
this.rootPage = TabsPage;
platform.ready().then(() => {
});
}
}
and this is my page1.js:
import {Page} from 'ionic-angular';
import {Http} from 'angular2/http';
#Page({
templateUrl: 'build/pages/page1/page1.html'
})
export class Page1 {
constructor(http:Http) {
this.mget = http.get("https://httpbin.org/ip")
.subscribe(data => {
var alert = Alert.create({
title: "Your IP Address",
subTitle: data.json().origin,
buttons: ["close"]
});
this.nav.present(alert);
}, error => {
console.log(JSON.stringify(error.json()));
});
}
}
When adding http:Http to the constructor -> constructor(http:Http) the whole app goes blank in browser...
And I get an error in Console:
Error: Cannot find module "../page1/page1"
I've also tried this in Page1.js:
export class Page1 {
constructor() {
}
makeGetRequest() {
this.http.get("https://httpbin.org/ip")
.subscribe(data => {
var alert = Alert.create({
title: "Your IP Address",
subTitle: data.json().origin,
buttons: ["close"]
});
this.nav.present(alert);
}, error => {
console.log(JSON.stringify(error.json()));
console.log('yolo')
alert('hello');
});
}
}
and then call makeGetRequest() on (click) in page1.html
but it returns these exeptions:
EXCEPTION: Error during evaluation of "click"
ORIGINAL EXCEPTION: TypeError: this.http is undefined
please help!
:)
-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
THIS IS THE SOLUTION:
page1.js:
import {Page} from 'ionic-angular';
import {Http} from 'angular2/http';
#Page({
templateUrl: 'build/pages/page1/page1.html'
})
export class Page1 {
static get parameters(){
return [Http];
}
constructor(http) {
this.http = http;
this.mget = this.http.get("https://httpbin.org/ip")
.subscribe(data => {
console.log(data);
}, error => {
console.log('faild');
});
}
}
app.js:
import {App, Platform} from 'ionic-angular';
import {TabsPage} from './pages/tabs/tabs';
import { HTTP_PROVIDERS } from 'angular2/http';
#App({
template: '<ion-nav [root]="rootPage"></ion-nav>',
providers: [HTTP_PROVIDERS],
config: {} // http://ionicframework.com/docs/v2/api/config/Config/
})
export class MyApp {
static get parameters() {
return [[Platform]];
}
constructor(platform) {
this.rootPage = TabsPage;
platform.ready().then(() => {
});
}
}
Please try this
export class Page1 {
static get parameters(){
return [Http];
}
constructor(http) {
this.http = http;
this.mget = this.http.get("https://httpbin.org/ip")
.subscribe(data => {
var alert = Alert.create({
title: "Your IP Address",
subTitle: data.json().origin,
buttons: ["close"]
});
this.nav.present(alert);
}, error => {
console.log(JSON.stringify(error.json()));
});
}
}
I would recommend you to write the get request inside a separate service and inject it in your page.
Also have a look at this - http://tphangout.com/?p=113
Detailed and simple instructions are given there for making a simple GET request from an Ionic 2 app.
I believe you need to
import { HTTP_PROVIDERS } from 'angular2/http';
in your app.js instead of HTTP_BINDINGS and change providers: [HTTP_BINDINGS] to providers: [HTTP_PROVIDERS]
See Angular2 docs