Should I test calls to my service inside BootstrapDialog prompt? - angularjs

I am using BootstrapDialog to show a dialog box. If the user clicks delete it calls my service and deletes it from the database. If they click cancel it closes the dialog.
I am writing unit tests and this one is puzzling me. The call to my service is nested pretty deep and I wouldn't even know how to make the tests know which path I'm testing.
My code in the controller:
$scope.deleteInventoryEntry = function(id){
//launch dialog
BootstrapDialog.show({
title: 'CONFIRM DELETION',
message: 'Are you sure you want to delete this record?',
closable: false,
type: BootstrapDialog.TYPE_DANGER,
buttons: [{
label: 'Cancel',
action: function(dialog) {
dialog.close();
}
}, {
label: 'Delete',
icon: 'glyphicon glyphicon-remove',
cssClass: 'btn-danger',
action: function(dialog) {
//remove item from database
tankService.deleteInventoryEntry(id).success(function (response) {
//remove item from table if successful
if(response > 0){
//figure out which item to remove from table
var pos = $scope.invTable.filtered.map(function(item) { return item._id; }).indexOf(id);
//remove from table
$scope.invTable.filtered.splice(pos,1);
$scope.selectedItem.lineItems = [];
dialog.close();
//$scope.successGrowl(' QC Deleted Successfully');
}
});
}
}
]
});
};
My Test
it('prompts on delete inventory item', function(){
spyOn(BootstrapDialog, 'show').and.callThrough();
$scope.deleteInventoryEntry(1);
expect(BootstrapDialog.show).toHaveBeenCalled();
});
I can also test if say the ID was NAN or Null and the dialog shouldn't show. But I'm just curious if I should be somehow testing tankService.deleteInventoryEntry() was called. I feel like I should but does that mean I have to mock this entire dialog item?
Any help to point me in the right direction would be much appreciated.
Thanks!

Rule of thumb to any testing. Don't test the implementation, but the behavior. For instance you should test that when you filled a form and clicked a submit button it was sent to your API and something happened in response. Tests should be independent from the view part as much as possible (eg. was the form located in a modal or somewhere in the page).

Related

Ionic 1 - Popup not working properly after state change

I have an $ionicPopup defined inside a particular controller say Controller-1. When I move to Controller-1 from any other different Controller-X by changing the state as $state.go('xxx.xx.xx'), the $ionicPopup is not working as expected. But at the same time, if I open Controller-1 for the first time, $ionicPopup works fine. State change is causing issue. How to solve it?
The code for $ionicPopup inside Controller-1 is:
$ionicPopup.show({
title: "Delivery Not Available",
subTitle: 'Selected area is beyond our delivering area. You can place only Take Away orders.',
scope: $scope,
buttons: [{
text: 'Cancel',
onTap: function(e) {
return true;
}
},
{
text: '<b>OK</b>',
type: 'button-balanced',
onTap: function(e) {
$state.go('home.app');
}
},
]});
If I directly launch it from Controller-1 for the first time, it works as expected:
Screenshot - Normal Case
But, if I move to Controller-1 from any other state through a state change using $state.go('xxx.xx.x'), it shows broken output:
Screenshot - Failing Case
Make a function like this for your popup and Call that Function in your success callback function and make sure you have this code in the same controller in which success callback is written
$scope.showConfirm = function() {
var confirmPopup = $ionicPopup.confirm({
title: 'Title',
template: 'Are you sure?'
});
confirmPopup.then(function(res) {
if(res) {
console.log('Sure!');
} else {
console.log('Not sure!');
}
});
};
Refer this link for more details on Ionic Popup

Ionic open modal in app.js

I am trying to implement Web intent and need to show popup when user shared something with my app and want to get some text.
if (window.plugins && window.plugins.webintent) {
var incomingURL;
window.plugins.webintent.getExtra(window.plugins.webintent.EXTRA_TEXT,function(url) {
incomingURL = url
var myPopup = messageAlert.saveData(url);
}, function() {
incomingURL = false;
}
);
Here messageAlert is a factory. I want to show a modal or popup where user can input some text and i can use furture.
.factory('messageAlert', function ($ionicPopup,$timeout,$ionicModal) {
return {
saveData : function(url) {
// here i tried different scenes. but nothing work out.
// i want a form where user can input some data and save
}
}
}
Can anybody give me idea
As far as I understand, your problem is not about getting the webintent to work, but simply to display the $ionicPopup.
So the main issue I see, is that you inject the $ionicPopup within a factory. Since you want to display the popup on a view, you need to inject it in your controller. There you can create a popup like this:
$ionicPopup.prompt({
title: 'Your title text',
template: 'Please enter your text here',
inputType: 'text',
inputPlaceholder: 'Your text here'
}).then(function(res) {
// after the user typed something, this result callback will be called
// res will contain the text which your user entered
});
You can find the corresponding docs with possible settings here.
Trying to merge this with your code above, I would suggest something like this:
.controller('YourCtrl', function($ionicPopup, messageAlert) {
this.someFunction = function() {
if (window.plugins && window.plugins.webintent) {
var incomingURL;
window.plugins.webintent.getExtra(window.plugins.webintent.EXTRA_TEXT,function(url) {
incomingURL = url;
// open the text input popup
var myPopup = $ionicPopup.prompt({
title: 'Your title text',
template: 'Please enter your text here',
inputType: 'text',
inputPlaceholder: 'Your text here'
});
// after the user typed something, this result callback will be called
myPopup.then(function(userText) {
// userText contains the text which your user entered
// now you can save the data with your factory
messageAlert.saveData(incomingURL, userText);
});
}, function() {
incomingURL = false;
});
}
};
});
Please note that I did not test the latter code, since I do not understand your exact use case.
Here are some ideas to debug the problem:
Does it work when you put a simple alert('YES THIS WORKS!') in the messageAlert.saveData() factory?
Are you sure that the app was invoked with the specified extra? See: https://github.com/Initsogar/cordova-webintent#hasextra

AngularJS UI router $onLocationChangeStart event.PreventDefault does not work

I need a way to interrupt a user navigating to a new page when there are unsaved changes on the current page. I implemented a modified version of the solution here:
http://weblogs.asp.net/dwahlin/cancelling-route-navigation-in-angularjs-controllers
However, what I see in the browser is that as soon as the user clicks on a link, the view changes and the new controller loads completely while the modal dialog is displayed. When the user clicks 'cancel' and event.preventDefault is fired, the user simply ends up on the new view. This is strange because everything I've read indicates that this is the accepted method, and nobody seems to have this issue. Yet I can't for the life of me see what is wrong with my code.
Here's the function in the main app for handling location changes (ModalService just wraps the angular bootstrap $modal service):
onRouteChangeOff = $rootScope.$on('$locationChangeStart', routeChange);
function routeChange(event, newUrl, oldUrl) {
//Navigate to newUrl if the form isn't dirty
if (!$rootScope.unsavedChanges) return;
var modalOptions = {
closeButtonText: 'Cancel',
actionButtonText: 'Ignore Changes',
headerText: 'Unsaved Changes',
bodyText: 'You have unsaved changes. Leave the page?'
};
ModalService.showModal({}, modalOptions).result.then(function () {
$rootScope.unsavedChanges = false;
$location.path(newUrl); //Go to page they're interested in
}
, function () {
event.preventDefault();
});
return;
}
Any ideas?
In case anyone else has this problem, the fix turned out to be quite simple. I moved the code to the stateChangeStart event. Code looks like this:
onRouteChangeOff = $rootScope.$on('$stateChangeStart', routeChange);
function routeChange(event, newState) {
//Navigate to newUrl if the form isn't dirty
if (!$rootScope.unsavedChanges) return;
event.preventDefault();
var modalOptions = {
closeButtonText: 'Cancel',
actionButtonText: 'Ignore Changes',
headerText: 'Unsaved Changes',
bodyText: 'You have unsaved changes. Leave the page?'
};
ModalService.showModal({}, modalOptions).result.then(function () {
$rootScope.unsavedChanges = false;
$state.go(newState); //Go to page they're interested in
});
}

Backbone Router and Views

I've been getting started with Backbone.js and trying to get my head around Routing to specific Views. In my HTML I have <a href="#project/1"> tags to render the View for the tasks of a project.
Query
When the link is clicked, it appends the task with the id to the DOM, however, when a second link is clicked, it appends that task underneath the previous. I'm not sure if its best practice to $.empty the View then call the show method?
A snippet of my Router:
routes: {
'project/:id: 'showtasks'
},
showtasks: function(id) {
Event.trigger('tasks:show', id);
}
Snippet of the Collection of Tasks
initialize: function() {
Event.on('tasks:show', this.show, this);
},
show: function() {
var task = this.collection.get(id);
var taskView = new App.Views.Task({ model: task });
this.$el.append(taskView.render().el);
}
The collection
var tasks = new App.Collections.Tasks([
{
id: 1,
title: 'First Task',
content: 'Lots of Content...'
},
{
id: 2,
title: 'Second Task',
content: 'Lots of Content...'
},
{
id: 3,
title: 'Third Task',
content: 'Lots of Content...'
}
]);
var tasksView = new App.Views.Tasks({ collection: tasks });
A couple of good design patterns for Backbone view is:
Calling render method multiple times should not have any side effect. It should render correctly.
When you use append in a render, you are basically setting up the flow of your view in the render method which should be basically the responsibility of the template of your view.
So I would suggest you should use this >
this.$el.html(taskView.render().el);
This would work perfectly fine however you would get into an issue if you have subviews. For that read this - (basically this whole answer is a shameless ripoff of this article :P )

extjs, php works on double click, and i want to work on one click

in my php code i have a table with rows:
name actions
category 1 edit
category 2 edit
..... edit
every edit buttons have an id for ex: <a href="edit('.$raw['id'].')" id="show-btn'.$raw['id'].'">
now my function edit is:
var win;
var button;
function edit(idd){
button = Ext.get('show-btn'+idd);
button.on('click', function(){
// create the window on the first click and reuse on subsequent clicks
if(!win){
win = new Ext.Window({
applyTo:'hello-win',
layout:'column',
closeAction:'hide',
plain: true,
autoHeight:true,
items: new Ext.FormPanel({
applyTo: 'hello-tabs',
}),
buttons: [{
text:'Modify',
handler: function(){
win.hide();
document.form_edit.submit();
}
},{
text: 'Close',
handler: function(){
win.hide();
}
}]
});
}
win.show(this);
});
};
This script works perfectly but on bauble click, how can i do this on one click.
I now the problem is on button.on('click')
Many thanks, please help me with this issues
It seems like some level of functionality is being added on your first click and then that is used for subsequent clicks. I'd suggest putting either an alert or a console.log (if your browser supports it) inside of the edit function both before and after the if(win) test.
I'm also curious -- if you're trying to have this execute for an entire table, wouldn't that win global variable cause problems?
I think you're better off pre-populating win and using a local variable:
function edit(idd){
button = Ext.get('show-btn'+idd);
// this can be moved into button.on('click', function(){, but that may be
// causing the problem...
// come to think of it... the example here:
// http://dev.sencha.com/deploy/dev/examples/window/hello.js
// has this inside the button.on method. You may want to try it both ways.
if(!button.win){
button.win = new Ext.Window({
applyTo:'hello-win',
layout:'column',
closeAction:'hide',
plain: true,
autoHeight:true,
items: new Ext.FormPanel({
applyTo: 'hello-tabs',
}),
buttons: [{
text:'Modify',
handler: function(){
win.hide();
document.form_edit.submit();
}
},{
text: 'Close',
handler: function(){
win.hide();
}
}]
});
}
button.on('click', function(){
// the window already exists, so no need to worry about creating it.
button.win.show(this);
});
};
Could that work? If not, we will need to see more code.

Resources