Unittesting in Karma : Keypress inside a text Input - javascript

Have a simple html text input:
<html>
<body>
<input id="inputstring" type="text" onkeypress="testcharacter" onKeyUp="testcharacter ">
<div id ="result"></div>
<script type="text/javascript" src="javascriptfile.js"></script>
<script>testcharacter .init();</script>
</body>
</html>
Would like to be able to run a unittest on each keypress that occurs inside this text input and check it against a particular key. Javascript file is:
'use strict';
window.testcharacter = window.testcharacter || {};
(function() {
var testcharacter = function(k){
var s = document.getElementById('inputstring').value
if(s!=null||s.trim()!=""){
if(k==65){
document.getElementById('result').innerHTML = 'You pressed A'
}
if(s.length==0)
{ document.getElementById('result').innerHTML = ''}
}
}
window.testcharacter.init = function(){
document.getElementById('inputstring').addEventListener('keyup', testcharacter );
document.getElementById('inputstring').addEventListener('keypress', testcharacter );
};
})();
A portion of my test file, concerning this portion I've got:
it('should display character count with each keypress', function(){
var triggerKeyDown = function(element,keycode){
var e = jQuery.Event("testcharacter");
e.which = keycode;
$(element).trigger(e)
var ee = jQuery.Event("keyup")
ee.which = keycode + 1;
$(element).trigger(ee)
r = document.getElementById('inputstring').value
expect(document.getElementById('result').innerHTML).toBe(expected);
expect(document.getElementById('inputstring').innerHTML).toBe(expected);
};
triggerKeyDown('inputstring','65')
});
Right now, the keypress and keyup don't seem to persist to the inputbox,(I can easily set the value using:
document.getElementById('inputstring').value = 'A' which passes other portions of the tests, but not the keypresses) although I can see that the events are firing:
Testing of values:
ALERT: <input id="inputstring" type="text"> // captured 'inputstring'
ALERT: '' // captured value of 'inputstring'.value and 'result'.innerHTML
ALERT: 65 // e.which value
ALERT: {type: 'keypress', timeStamp: 1474058737895, jQuery31006431827041498093: true, which: 65} // event created
Not sure how to:
(1) fire off keypress/keyup events in Jasmine
(2) apply those events to the specific html element.value
So I can test those values. I would like each keypress/keyup to be tested.

I found somthing that you can try, replace element\eventName with your elements and events(keypress/keyup):-
var jasmineExtensions = {
jQuerySpies: {},
spyOnEvent: function(element, eventName) {
var control = {
triggered: false
};
element.bind(eventName, function() {
control.triggered = true;
});
jasmineExtensions.jQuerySpies[element[eventName]] = control;
};
};
var spyOnEvent = jasmineExtensions.spyOnEvent;
beforeEach(function() {
this.addMatchers({
toHaveBeenTriggered: function() {
var control = jasmineExtensions.jQuerySpies[this.actual];
return control.triggered;
}
});
});
may be can this help you.

I think you should use Jquery Simulate module
jQuery.simulate() - Simulate events to help unit test user interactions.

Related

jQuery script works only once, then TypeError: $(...) is not a function

I've downloaded this script for use conditional fields in forms:
(function ($) {
$.fn.conditionize = function(options) {
var settings = $.extend({
hideJS: true
}, options );
$.fn.showOrHide = function(is_met, $section) {
if (is_met) {
$section.slideDown();
}
else {
$section.slideUp();
$section.find('select, input').each(function(){
if ( ($(this).attr('type')=='radio') || ($(this).attr('type')=='checkbox') ) {
$(this).prop('checked', false).trigger('change');
}
else{
$(this).val('').trigger('change');
}
});
}
}
return this.each( function() {
var $section = $(this);
var cond = $(this).data('condition');
// First get all (distinct) used field/inputs
var re = /(#?\w+)/ig;
var match = re.exec(cond);
var inputs = {}, e = "", name ="";
while(match !== null) {
name = match[1];
e = (name.substring(0,1)=='#' ? name : "[name=" + name + "]");
if ( $(e).length && ! (name in inputs) ) {
inputs[name] = e;
}
match = re.exec(cond);
}
// Replace fields names/ids by $().val()
for (name in inputs) {
e = inputs[name];
tmp_re = new RegExp("(" + name + ")\\b","g")
if ( ($(e).attr('type')=='radio') || ($(e).attr('type')=='checkbox') ) {
cond = cond.replace(tmp_re,"$('" + e + ":checked').val()");
}
else {
cond = cond.replace(tmp_re,"$('" + e + "').val()");
}
}
//Set up event listeners
for (name in inputs) {
$(inputs[name]).on('change', function() {
$.fn.showOrHide(eval(cond), $section);
});
}
//If setting was chosen, hide everything first...
if (settings.hideJS) {
$(this).hide();
}
//Show based on current value on page load
$.fn.showOrHide(eval(cond), $section);
});
}
}(jQuery));
I'm trying this because I need to use conditionize() in one of my tabs and when I reload the tab, all works but if I go to other tab and I return to the previous tab(where I need this works), I get that error.
When I change tabs, I'm only reloading one part of the page.
When I load the page this works perfectly, but if I try to call function again from browser console, it tells me that TypeError: $(...)conditionize() is not a function.
I have included the script in header tag and I'm calling it with this script on the bottom of body:
<script type="text/javascript">
$('.conditional').conditionize();
</script>
EDIT:
I have written
<script type="text/javascript">
console.log($('.conditional').conditionize);
setTimeout(function () {console.log($('.conditional').conditionize);}, 2);
</script>
and this print me at console the function, and when 2 milliseconds have passed, it print me undefined
I have found the solution.
Because any reason, the $ object and jQuery object are not the same in my code.
I have discovered it using this on browser console:
$===jQuery
This return false (This was produced because in other JS, I was using the noConflict(), which give me the problem)
Explanation: noConflict()
So I have solved it changing the last line of my JS by:
//Show based on current value on page load
$.fn.showOrHide(eval(cond), $section);
});
}
}($));
Putting the $ instead of 'jQuery'

Jasmine test event listener function for nodeList from given selector

I have following code which listens for keydown event in given array of nodeList.
var obj = {
method: function(nodeSelector) {
var nodeContainers = document.querySelectorAll(nodeSelector);
var keyListenerFunc = this.keyListener.bind(this);
this.bindListener(nodeContainers, keyListenerFunc);
},
isListNode: function (evt){
return evt.target.nodeName.toLowerCase() === 'li';
},
isContainer: function(evt){
return evt.target.parentNode.classList.contains(this.indicatorClass);
},
keyListener: function(evt) {
if (evt.keyCode === 32 && (this.isContainer(evt) && this.isListNode(evt))) {
evt.preventDefault();
evt.target.click();
}
},
bindListener: function(targets, callbackFunc) {
[].forEach.call(targets, function(item) {
item.addEventListener('keydown', callbackFunc);
});
},
indicatorClass: 'indicator'
};
I'm using it like: obj.method('.someClassNames');
But now I want to test it completely including the triggering of keydown event. How can I attach event listener and then trigger keydown event on given dom nodes so that my Jasmine tests would work ? How can I create some dummy html code here and then trigger event on it ? I am expecting to write tests of this type =>
it('It should put event listeners on each carousel passed to the service', function(){});
it('It should call event.preventDefault', function(){});
it('It should call event.target.click', function(){});
My markup is follwing
var html = '<div class="someClassNames">'+
'<div class="indicator">'+
'<li>text</li>'+
'</div>'
'</div>';
I am assuming that I am going to need to trigger following keydown event but I am not sure as to how to trigger is on the given markup and check in the test description.
var e = new window.KeyboardEvent('keydown', {
bubbles: true
});
Object.defineProperty(e, 'keyCode', {'value': 32});
I am very much new to testing with Jasmine and I couldn't find any examples that would help me test this scenario. I hope my example makes it clear.
few observations:
Note that the callbackFunc is actually assigned to the onkeydown
attribute of the element. Hence you may want to spy on the
element.onkeydown rather than obj.keyListener
Sometimes the render of the UI element may take place after spec has
been run.
So to ensure that you have the element is present, I've used the
setTimeout with a jasmine clock
If you really want to test your obk.keyListener, try using an
anonymous function like here
here is how I've it running. I've used mouseover as I'm lazy :)
var obj = {
testVar : "Object",
method: function(nodeSelector) {
var nodeContainers = document.querySelectorAll(nodeSelector);
var keyListenerFunc = this.keyListener.bind(this);
this.bindListener(nodeContainers, keyListenerFunc);
},
isListNode: function(evt) {
return evt.target.nodeName.toLowerCase() === 'li';
},
isContainer: function(evt) {
return evt.target.parentNode.classList.contains(this.indicatorClass);
},
keyListener: function(evt) {
console.log('Yo! You hovered!');
},
bindListener: function(targets, callbackFunc) {
targets.forEach(function(item) {
item.addEventListener('mouseover', callbackFunc, false);
});
},
indicatorClass: 'indicator'
};
describe('Sample tests', function() {
//this ensures you have the element set up
beforeEach(function() {
jasmine.clock().install();
jasmine.DEFAULT_TIMEOUT_INTERVAL = 200;
setTimeout(function() {
obj.method('div.indicator');
}, 0);
});
it('It should put event listeners', function() {
jasmine.clock().tick(10);
var ele= document.getElementsByClassName("indicator")[0];
spyOn(ele, 'onmouseover').and.callThrough();
$('.indicator').trigger('mouseover');
expect(ele.onmouseover).toHaveBeenCalled();
expect(typeof ele.onmouseover).toBe('function');
});
});
HTML CONTENT:
<div class="someClassNames">
<div class="indicator">
<li>text</li>
<br/> </div>
</div>

Javascript OnFocus and OnBlue events

I want to create onfocus and onBlur events in Javascript. I have a found a solution which is in jQuery but not sure how to convert it into Javascript.
<textarea id='postitTextArea' ></textarea>
$('input,textarea').focus(function()
{
$(this).data('placeholder', $(this).attr('placeholder'))
.attr('placeholder', '');
}).blur(function()
{
$(this).attr('placeholder', $(this).data('placeholder'));
});
The above code is working fine. Any help would be appreciated.
Try like this:
var obj = document.getElementById('postitTextArea');
obj.removeAttribute('onfocus');
obj.removeAttribute('onblur');
obj.addEventListener('focus', function() {
// your js code for focus event
});
obj.addEventListener('blur', function() {
// your js code for blur event
});
var inputs, index;
inputs = document.getElementsByTagName('input');
for (index = 0; index < inputs.length; ++index) {
inputs[index].onfocus = function(){
// Your script
};
inputs[index].onblur = function(){
// Your script
};
}
//similarly for textarea
This is the JavaScript code for the jQuery code you have provided,
HTML:
<textarea id='postitTextArea' placeholder="placeholder text" onfocus="focusAction()" onblur="blurAction()"></textarea>
JS:
function focusAction(){
var target = document.getElementById('postitTextArea');
var setAttrVal = target.setAttribute('placeholder',' ');
console.log('placeholder: ' + setAttrVal);
}
function blurAction(){
var target = document.getElementById('postitTextArea');
var setAttrVal = target.setAttribute('placeholder','placeholder text');
console.log('placeholder: ' + setAttrVal);
}

NotFoundError: DOM Exception 8 when substituting innerHTML

I'm new to js-development. I have the following code:
<html>
<body>
<div><span id="inline">Click here to start editing</span></div>
<script>
var inline = document.getElementById("inline");
inline.onclick = function() {
if (!inline.editable) {
var text = inline.innerText;
inline.innerHTML = "<input type='text' id='inline-editable'>";
inline.editable = true;
var inline_editable = document.getElementById("inline-editable");
inline_editable.value = text;
inline_editable.onblur = function() {
var value = inline_editable.value;
inline.editable = false;
inline.innerHTML = value;
}
inline_editable.onkeypress = function(event) {
if (event.keyCode == 13) {
inline_editable.onblur();
}
}
}
}
</script>
</body>
</html>
Which shows some text inside span and allows inline editing. When I finish editing within just onblur event it work perfectly fine. But if I want to terminate editing by Enter and use the same hander I get an error NotFoundError: DOM Exception 8 in this line:
inline.innerHTML = value;
Nevertheless everything works as I expect. Can anyone help me to avoid this error?
I assume that is happened because I destroy inline-editable element while event handling is not finished and it wants to invoke onchange maybe. Should I have 2 controls all the time an switch their visibility instead?
Problem here is the onblur is triggered twice, the second time, the element is not there which causes the problem. Kill the events
var inline = document.getElementById("inline");
inline.onclick = function() {
if (!inline.editable) {
var text = inline.innerText;
inline.innerHTML = "<input type='text' id='inline-editable'>";
inline.editable = true;
var inline_editable = document.getElementById("inline-editable");
inline_editable.value = text;
inline_editable.onblur = function() {
this.onblur = function(){};
var value = this.value;
inline.editable = false;
inline.innerHTML = value;
}
inline_editable.onkeypress = function(event) {
if (event.keyCode == 13) {
this.onblur();
}
}
}
}

Detect when input box filled by keyboard and when by barcode scanner.

How I can programmatically detect when text input filled by typing on keyboard and when it filled automatically by bar-code scanner?
I wrote this answer, because my Barcode Scanner Motorola LS1203 generated keypress event, so I can't use Utkanos's solution.
My solution is:
var BarcodeScanerEvents = function() {
this.initialize.apply(this, arguments);
};
BarcodeScanerEvents.prototype = {
initialize: function() {
$(document).on({
keyup: $.proxy(this._keyup, this)
});
},
_timeoutHandler: 0,
_inputString: '',
_keyup: function (e) {
if (this._timeoutHandler) {
clearTimeout(this._timeoutHandler);
this._inputString += String.fromCharCode(e.which);
}
this._timeoutHandler = setTimeout($.proxy(function () {
if (this._inputString.length <= 3) {
this._inputString = '';
return;
}
$(document).trigger('onbarcodescaned', this._inputString);
this._inputString = '';
}, this), 20);
}
};
Adapted the super useful Vitall answer above to utilize an IIFE instead of prototyping, in case anyone just seeing this now is into that.
This also uses the 'keypress' event instead of keyup, which allowed me to reliably use KeyboardEvent.key, since KeyboardEvent.which is deprecated now. I found this to work for barcode scanning as well as magnetic-strip card swipes.
In my experience, handling card swipes with keyup caused me to do extra work handling 'Shift' keycodes e.g. a Shift code would be followed by the code representing '/', with the intended character being '?'. Using 'keypress' solved this as well.
(function($) {
var _timeoutHandler = 0,
_inputString = '',
_onKeypress = function(e) {
if (_timeoutHandler) {
clearTimeout(_timeoutHandler);
}
_inputString += e.key;
_timeoutHandler = setTimeout(function () {
if (_inputString.length <= 3) {
_inputString = '';
return;
}
$(e.target).trigger('altdeviceinput', _inputString);
_inputString = '';
}, 20);
};
$(document).on({
keypress: _onKeypress
});
})($);
Well a barcode won't fire any key events so you could do something like:
$('#my_field').on({
keypress: function() { typed_into = true; },
change: function() {
if (typed_into) {
alert('type');
typed_into = false; //reset type listener
} else {
alert('not type');
}
}
});
Depending on when you want to evaluate this, you may want to do this check not on change but on submit, or whatever.
you can try following example, using jQuery plugin https://plugins.jquery.com/scannerdetection/
Its highly configurable, time based scanner detector. It can be used as solution for prefix/postfix based, time based barcode scanner.
Tutorial for usage and best practices, as well discussed about various Barcode Scanner Models and how to deal with it. http://a.kabachnik.info/jquery-scannerdetection-tutorial.html
$(window).ready(function(){
//$("#bCode").scannerDetection();
console.log('all is well');
$(window).scannerDetection();
$(window).bind('scannerDetectionComplete',function(e,data){
console.log('complete '+data.string);
$("#bCode").val(data.string);
})
.bind('scannerDetectionError',function(e,data){
console.log('detection error '+data.string);
})
.bind('scannerDetectionReceive',function(e,data){
console.log('Recieve');
console.log(data.evt.which);
})
//$(window).scannerDetection('success');
<input id='bCode'type='text' value='barcode appears here'/>
For ES6 2019 version of Vitall answer.
const events = mitt()
class BarcodeScaner {
initialize = () => {
document.addEventListener('keypress', this.keyup)
if (this.timeoutHandler) {
clearTimeout(this.timeoutHandler)
}
this.timeoutHandler = setTimeout(() => {
this.inputString = ''
}, 10)
}
close = () => {
document.removeEventListener('keypress', this.keyup)
}
timeoutHandler = 0
inputString = ''
keyup = (e) => {
if (this.timeoutHandler) {
clearTimeout(this.timeoutHandler)
this.inputString += String.fromCharCode(e.keyCode)
}
this.timeoutHandler = setTimeout(() => {
if (this.inputString.length <= 3) {
this.inputString = ''
return
}
events.emit('onbarcodescaned', this.inputString)
this.inputString = ''
}, 10)
}
}
Can be used with react hooks like so:
const ScanComponent = (props) => {
const [scanned, setScanned] = useState('')
useEffect(() => {
const barcode = new BarcodeScaner()
barcode.initialize()
return () => {
barcode.close()
}
}, [])
useEffect(() => {
const scanHandler = code => {
console.log(code)
setScanned(code)
}
events.on('onbarcodescaned', scanHandler)
return () => {
events.off('onbarcodescaned', scanHandler)
}
}, [/* here put dependencies for your scanHandler ;) */])
return <div>{scanned}</div>
}
I use mitt from npm for events, but you can use whatever you prefer ;)
Tested on Zebra DS4208
The solution from Vitall only works fine if you already hit at least one key. If you don't the first character will be ignored (if(this._timeoutHandler) returns false and the char will not be appended).
If you want to begin scanning immediately you can use the following code:
var BarcodeScanerEvents = function() {
this.initialize.apply(this, arguments);
};
BarcodeScanerEvents.prototype = {
initialize : function() {
$(document).on({
keyup : $.proxy(this._keyup, this)
});
},
_timeoutHandler : 0,
_inputString : '',
_keyup : function(e) {
if (this._timeoutHandler) {
clearTimeout(this._timeoutHandler);
}
this._inputString += String.fromCharCode(e.which);
this._timeoutHandler = setTimeout($.proxy(function() {
if (this._inputString.length <= 3) {
this._inputString = '';
return;
}
$(document).trigger('onbarcodescaned', this._inputString);
this._inputString = '';
}, this), 20);
}
};
If you can set a prefix to your barcode scanner I suggests this (I changed a bit the Vitall code):
var BarcodeScanner = function(options) {
this.initialize.call(this, options);
};
BarcodeScanner.prototype = {
initialize: function(options) {
$.extend(this._options,options);
if(this._options.debug) console.log("BarcodeScanner: Initializing");
$(this._options.eventObj).on({
keydown: $.proxy(this._keydown, this),
});
},
destroy: function() {
$(this._options.eventObj).off("keyup",null,this._keyup);
$(this._options.eventObj).off("keydown",null,this._keydown);
},
fire: function(str){
if(this._options.debug) console.log("BarcodeScanner: Firing barcode event with string: "+str);
$(this._options.fireObj).trigger('barcode',[str]);
},
isReading: function(){
return this._isReading;
},
checkEvent: function(e){
return this._isReading || (this._options.isShiftPrefix?e.shiftKey:!e.shiftKey) && e.which==this._options.prefixCode;
},
_options: {timeout: 600, prefixCode: 36, suffixCode: 13, minCode: 32, maxCode: 126, isShiftPrefix: false, debug: false, eventObj: document, fireObj: document},
_isReading: false,
_timeoutHandler: false,
_inputString: '',
_keydown: function (e) {
if(this._input.call(this,e))
return false;
},
_input: function (e) {
if(this._isReading){
if(e.which==this._options.suffixCode){
//read end
if(this._options.debug) console.log("BarcodeScanner: Read END");
if (this._timeoutHandler)
clearTimeout(this._timeoutHandler);
this._isReading=false;
this.fire.call(this,this._inputString);
this._inputString='';
}else{
//char reading
if(this._options.debug) console.log("BarcodeScanner: Char reading "+(e.which));
if(e.which>=this._options.minCode && e.which<=this._options.maxCode)
this._inputString += String.fromCharCode(e.which);
}
return true;
}else{
if((this._options.isShiftPrefix?e.shiftKey:!e.shiftKey) && e.which==this._options.prefixCode){
//start reading
if(this._options.debug) console.log("BarcodeScanner: Start reading");
this._isReading=true;
this._timeoutHandler = setTimeout($.proxy(function () {
//read timeout
if(this._options.debug) console.log("BarcodeScanner: Read timeout");
this._inputString='';
this._isReading=false;
this._timeoutHandler=false;
}, this), this._options.timeout);
return true;
}
}
return false;
}
};
If you need you customize timeout, suffix, prefix, min/max ascii code readed:
new BarcodeScanner({timeout: 600, prefixKeyCode: 36, suffixKeyCode: 13, minKeyCode: 32, maxKeyCode: 126});
I also added the isShiftPrefix option to use for example the $ char as prefix with these options: new BarcodeScanner({prefixKeyCode: 52, isShiftPrefix: true});
This is a fiddle: https://jsfiddle.net/xmt76ca5/
You can use a "onkeyup" event on that input box. If the event has triggered then you can called it "Input from Keyboard".
$(window).ready(function(){
//$("#bCode").scannerDetection();
console.log('all is well');
$(window).scannerDetection();
$(window).bind('scannerDetectionComplete',function(e,data){
console.log('complete '+data.string);
$("#bCode").val(data.string);
})
.bind('scannerDetectionError',function(e,data){
console.log('detection error '+data.string);
})
.bind('scannerDetectionReceive',function(e,data){
console.log('Recieve');
console.log(data.evt.which);
})
//$(window).scannerDetection('success');
<input id='bCode'type='text' value='barcode appears here'/>
Hi I have and alternative solution for evaluate a result of the bar code scanner without use of jQuery, first you need and input text that have a focus the moment that the barcode scanner is works
<input id="input_resultado" type="text" />
The code in JavaScript is:
var elmInputScan = document.getElementById('input_resultado');
elmInputScan.addEventListener('keypress', function (e){
clearInterval( timer_response );
timer_response = setTimeout( "onInputChange()", 10);
});
When the barcode scanner input the text call serveral times to the keypress event, but only I interested to the final result, for this reason I use the timer. That's all, you can process the value into the onInputChange function.
function onInputChange() {
console.log( document.getElementById('input_resultado').value );
}
document.addEventListener("keypress", function (e) {
if (e.target.tagName !== "INPUT") {
// it's your scanner
}
});
None of the solutions worked for me because I don't want to focus on an input. I want the result page(item details page) to keep listening for the scanner to scan next item. My scanner fires the keypress event so this worked like a charm for me.
var inputTemp = '';
var inputTempInterval = setInterval(function() {
// change 5 as minimum length of the scan code
if (inputTemp.length >= 5) {
var detected = inputTemp;
inputTemp = '';
clearInterval(inputTempInterval); // stop listening if you don't need anymore
onScannerTrigger(detected);
} else {
inputTemp = '';
}
}, 100);
$(window).keypress(function(e){
inputTemp += String.fromCharCode(e.which);
});
function onScannerTrigger(scannedCode) {
console.log(scannedCode);
// do your stuff
}
I have published a lightweight JS package which doesn't rely on jQuery or input fields. It simple looks at the timing of the keyPress-events to determine wether it was a barcode scanner or regular input.
https://www.npmjs.com/package/#itexperts/barcode-scanner
import {BarcodeScanner} from "#itexperts/barcode-scanner";
let options = {
timeOut: 130,
characterCount: 13
}
let barcodeScanner = new BarcodeScanner(options);
barcodeScanner.addEventListener('scan', function(e){
let barcode = e.detail;
console.log(barcode);
});
I highly recommend this js plugin https://github.com/axenox/onscan.js
It's easy to use and has tones of options to meet your need.
<script src="path-to-onScan.js"></script>
<script>
// Initialize with options
onScan.attachTo(document, {
suffixKeyCodes: [13], // enter-key expected at the end of a scan
reactToPaste: true, // Compatibility to built-in scanners in paste-mode (as opposed to keyboard-mode)
onScan: function(sCode, iQty) { // Alternative to document.addEventListener('scan')
console.log('Scanned: ' + iQty + 'x ' + sCode);
},
onKeyDetect: function(iKeyCode){ // output all potentially relevant key events - great for debugging!
console.log('Pressed: ' + iKeyCode);
}
});
</script>

Categories

Resources