How to test a directive with a scroll event? - angularjs-directive

I'm trying to test a directive that allows me to do infinite scroll in my app. The directive works fine in my production code.
This question is really close to How to test AngularJS Directive with scrolling and if someone says they are identical questions, I'll delete this one. However, it is not solving my problem (at least I cannot figure out how).
Here's my directive:
(function(){
"use strict";
angular.module('moduleA').directive('whenScrolled',Directive);
Directive.$inject = [];
function Directive(){
return {
restrict : 'A', // Restrict to Attributes
link : postLink
};
// Bind the element's scroll event
function postLink(scope,elem,attr){
var raw = elem[0];
elem.bind('scroll',eventMethod);
function eventMethod(){
if ( raw.scrollTop + raw.offsetHeight >= raw.scrollHeight ) {
scope.$apply(attr.whenScrolled);
}
};
};
};
})();
And here's my Karma test:
describe('whenScrolled Directive:',function(){
beforeEach(module('moduleA'));
// Create a list in HTML and force overflow for a scroll event
var html =
'<div id="test" ' +
' style="max-height:30px;overflow-y:scroll;" ' +
' when-scrolled="testScroll()">' +
' <ul>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' </ul>' +
'</div>';
var $rootScope,
element;
beforeEach(inject([
'$compile','$rootScope',
function($compile,$rs){
$rootScope = $rs;
$rootScope.isScrolled = false;
$rootScope.testScroll = function(){
$rootScope.isScrolled = true;
};
element = $compile(html)($rootScope);
$rootScope.$digest();
}
]));
it('should activate on scroll',function(){
expect(element).toBeDefined();
$rootScope.$broadcast('scroll'); <-- does nothing? -->
expect($rootScope.isScrolled).toBe(true); <-- fails -->
});
});
My karma.conf.js:
// Created May 04, 2016
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'vendor/angular/angular.js',
'vendor/angular/angular-mocks.js',
'moduleA/moduleA.view.html',
'moduleA/moduleA.module.js',
'moduleA/moduleA.controller.js',
'moduleB/moduleB.view.html',
'moduleB/moduleB.module.js',
'moduleB/moduleB.controller.js',
'test/module-A-tests.js',
'test/module-B-tests.js'
],
// list of files to exclude
exclude: [
'vendor/bootstrap*',
'vendor/jquery*',
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma- preprocessor
preprocessors: {
// karma-ng-html2js-preprocessor for templates in directives
'moduleA/moduleA.form.view.html': 'ng-html2js',
'moduleB/moduleB.view.html': 'ng-html2js'
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}
My thought was that if I have $rootScope broadcast 'scroll' out to all of it's children (in my case, $scope), and $scope was tied to my element with my directive, then that would trigger the method I provided (doScrollFn), and $scope.isScrolled would change.
But as far as I can tell, the doScrollFn never gets triggered.

I'm still not 100% sure what I did wrong, but after googling karma-ng-html-preprocessor, I found this:
https://www.npmjs.com/package/karma-ng-html2js-preprocessor-with-templates which lead me to:
https://github.com/vojtajina/ng-directive-testing which lead me to:
https://angularjs.org/#create-components and mainly, this:
https://github.com/vojtajina/ng-directive-testing/blob/start/test/tabsSpec.js.
And here's my test that works:
describe('whenScrolled Directive',function(){
beforeEach(module('moduleA'));
var html =
'<div id="test" ' +
' style="min-height:5px;max-height:30px;overflow-y:scroll;" ' +
' when-scrolled="testScroll()">' +
' <ul>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' <li>Testing</li>' +
' </ul>' +
'</div>';
var $rootScope,
element;
beforeEach(function(){
inject([
'$compile','$rootScope',
function($compile,$rs){
$rootScope = $rs;
$rootScope.isScrolled = false;
$rootScope.testScroll = function(){
$rootScope.isScrolled = true;
};
element = angular.element(html);
$compile(element)($rootScope);
$rootScope.$digest();
}
]);
});
it('should activate on scroll',function(){
expect(element.find('li').length).toBe(10);
element.triggerHandler('scroll');
expect($rootScope.isScrolled).toBe(true);
});
});
If this saves anybody some time and trouble, then it was worth the write-up.

Related

Unexpected Error: write callback called multiple times executing Gulp.js tasks inside a loop

I'm trying to execute some gulp tasks inside a map loop to generate css files for each sass theme I defined.
Finally, I return all merged tasks with merge-stream package.
Unfortunately, callback seems sent for each iteration / gulp task of my loop and i'm getting the following message :
Error: write callback called multiple times
#Task('build')
build() {
var self = this;
var tasks = this.themes.map((theme) => {
this.foldersXml = getAttributes('jntFolderWithExternalProvider');
this.filesXml = getAttributes('jntFolder');
this.foldersXml[theme] = []; this.foldersXml[theme].push(getAttributes('jntFolderWithExternalProvider'));
this.filesXml[theme] = []; this.filesXml[theme].push(getAttributes('jntFolderWithExternalProvider'));
fs.readFile(this.themesFolder + '/' + theme + '/' + this.fileName, 'utf8', (err: Error, data: string & Buffer) => {
if (err) {
throw new gutil.PluginError({
plugin: 'build',
message: 'Main file doesn\'t exist for the theme: ' + theme
});
} else {
var vars = data.match(/\$(.*?)\:/g);
this.requiredVars.map(requiredVar => {
if(vars !== null){
if(!vars.contains(requiredVar)){
throw new gutil.PluginError({
plugin: 'build',
message: 'Required variable ' + requiredVar + ' is not defined'
});
};
}
});
}
});
return gulp.src(this.templatesFolder + '/' + this.pattern)
.pipe(header('#import \'' + this.themesFolder + '/' + theme + '/' + this.fileName + '\';'))
.pipe(sass.sync().on('error', gutil.log))
.pipe(rename(function (path: any) {
var file = path.basename + path.extname;
var folderXml = getAttributes('jntFolderWithExternalProvider') as any;
folderXml[file] = [ getAttributes('jntCssFile') ];
self.foldersXml[theme][0][file] = []; self.foldersXml[theme][0][file].push(folderXml);
var fileXml = getAttributes('jntFolderWithExternalProvider') as any;
fileXml['jcr:content'] = [ getAttributes('jntRessource') ];
self.filesXml[theme][0][file] = []; self.filesXml[theme][0][file].push(fileXml);
path.dirname += '/' + file;
}))
.pipe(gulp.dest(this.themesFolder + '/' + theme))
});
return merge(tasks);
}
#SequenceTask()
run(){
return ['build'];
}
I just want to get rid of this message and be sure that callback is invoked only at the end. What's the best approach for you ?

Best way to load/store ndDialog template?

I am using ngDialog to display a modal window with an alert message.
I have the modal window working however the code looks messy.
I am wondering is it possible to replace the raw html template with a link to a html file instead of writing the HTML within the function itself.
$scope.openDialog = function(components) {
$scope.selected = components.component;
ngDialog.open({
template: '<h4>' + 'Alert' + '</h4>' +
'<table class="table">' +
'<tr><th>Type</th><td>' + components.type + '</td></tr>' +
'<tr><th>Component</th><td>' + components.component + '</td></tr>' +
'<tr><th>Created</th><td>' + components.created + '</td></tr>' +
'<tr><th>Alert</th><td>' + components.alert + '</td></tr>'+
'</table>',
plain: true
});
};
Yep, just use templateUrl rather than template.
$scope.openDialog = function(components) {
$scope.selected = components.component;
ngDialog.open({
templateUrl: 'yourfilename.html',
plain: true
});
};

Need help parsing json data on web page

I would like to display the last 10 songs played on a web page. Here's the code I'm using but I'm unable to retrieve and display the requested info from the json file. https://jsfiddle.net/Heropiggy95/6qkv7z3b/ Can someone point out the errors or demonstrate a working example? Thank you.
$(document).ready(function() {
$.getJSON("http://apps.radioactive.sg/lastsongsplayed.php?stationId=995fm&callback=myfunction", function(data) {
var html = ''; // declare the variable that will be used to store the information
$.each(data.myfunction, function(i, item) {
{
html += 'Song: ' + item.song.track + ', Artist: ' + item.song.artist + ', Time: ' + item.playedtime + '';
} //
}); //
$('.nowplaying').append(html); // print the information to the document with a span class of 'nowplaying' and use the jQuery append method to insert the information.
}); // close JSON call
}); // close document ready function
Try to use JSON.parse like this
$(document).ready(function() {
$.getJSON("http://apps.radioactive.sg/lastsongsplayed.php?stationId=995fm", function(data) {
var html = ''; // declare the variable that will be used to store the information
var parsedData = JSON.parse(data);
$.each(parsedData, function(i, item) {
{
html += 'Song: ' + item.song.track + ', Artist: ' + item.song.artist + ', Time: ' + item.playedtime + '';
} //
}); //
$('.nowplaying').append(html); // print the information to the document with a span class of 'nowplaying' and use the jQuery append method to insert the information.
}); // close JSON call
}); // close document ready function

Phonegap - HTML does not display image from storage

I'm using FileTransfer plugin to download an image from a remote server.
The image is stored in "storage/emulated/0/" - using the following code:
function downloadFile(fileDownloadName){
//console.log('downloadFile');
window.requestFileSystem(
LocalFileSystem.PERSISTENT,
0,
function(fileSystem){
//console.log('onRequestFileSystemSuccess');
fileSystem.root.getFile(
'dummy.html',
{create: true, exclusive: false},
function(fileEntry){
//console.log('onGetFileSuccess!');
var path = fileEntry.toURI().replace('dummy.html', '');
var fileTransfer = new FileTransfer();
fileEntry.remove();
fileTransfer.download(
'https://www.myserver.com/imagens/' + fileDownloadName,
path + fileDownloadName,
function(file) {
//console.log('download complete: ' + file.toURI());
return path + fileDownloadName;
},
function(error) {
console.log('download error source ' + error.source);
console.log('download error target ' + error.target);
console.log('upload error code: ' + error.code);
}
);
},
fail
);
},
fail
);
}
So, I'm pointing the path + fileDownloadName to SRC of my image, but the image does not appear.
var location = path + fileDownloadName;
window.resolveLocalFileSystemURL(location, function(oFile) {
oFile.file(function(readyFile) {
var reader= new FileReader();
reader.onloadend= function(evt) {
$(".circle-perfil").css('background-image', 'url(' + evt.target.result + ')');
};
reader.readAsDataURL(readyFile);
});
Is there something wrong I'm doing? Should I have to store the image in another folder? If yes, how to do it?
If you are using img tag then set its image as:
$(".circle-perfil").css('background', 'url(' + evt.target.result + ')');
or
$(".circle-perfil").css('content', 'url(' + evt.target.result + ')');
In case of div or span
$(".circle-perfil").css('background-image', 'url(' + evt.target.result + ')');
I had set it like:
var image = document.getElementById('cameraPic');
image.src = 'path/of/image file';

How to add a slide / image to a slide in Google Drive SDK javascript

I've been struggling with this for a little while now and couldn't find anything about it, anyone have any suggestions?
Is there a good way to add a slide to a presentation in Drive via the Google drive sdk for javascript? A way to add an image to a presentation as a new slide would be awesome. I've tried the following, with a constant 500 error.
function updateFile(fileId, imgurl, callback) {
const
boundary = '--314159265358979323846';
const
delimiter = "\r\n--" + boundary + "\r\n";
const
close_delim = "\r\n--" + boundary + "--";
var contentType = 'application/octet-stream';
var metadata = {
'title' : 'test pres'
};
console.log(imgurl);
var base64Data = imgurl.replace("data:image/png;base64,", "");
var multipartRequestBody = delimiter
+ 'Content-Type: application/json\r\n\r\n'
+ JSON.stringify(metadata) + delimiter + 'Content-Type: '
+ contentType + '\r\n' + 'Content-Transfer-Encoding: base64\r\n'
+ '\r\n' + base64Data + close_delim;
var request = gapi.client.request({
'path' : '/upload/drive/v2/files/' + fileId,
'method' : 'PUT',
'params' : {
'uploadType' : 'multipart',
'alt' : 'json'
},
'headers' : {
'Content-Type' : 'multipart/mixed; boundary="' + boundary + '"'
},
'body' : multipartRequestBody
});
if (!callback) {
callback = function(file) {
console.log(file)
};
}
request.execute(callback);
}
I've confirmed that I can create a presentation and image file with other code as well, but I can't seem to find a way to add to a presentation.
Anyone have any idea if this is possible?
Thanks,
Mike
The Drive SDK only supports whole-file operations. Anything which you want to do within a file is unsupported. For example, in the case of spreadsheets there is a separate spreadsheets API to manipulate rows and cells. For presentations, I'm not aware of an equivalent API.