I have 20 tests with the same check/expections and I want to reduce the repeated function in the end method call. Any suggestions to refactor?
describe('my test 1', function() {
it('response with email id reference expected', function(done) {
request
.post(apiPath)
.send(input)
.end(function(err, res) {
expect(res.statusCode).equals(200);
expect(res.body.refId.length == 36);
expect(res.body.this1.length = 1);
expect(res.body.that2.length = 2);
expect(res.body.that3.length = 3);
done();
});
});
});
I managed it like this below. If there is better way then please answer.
let positiveAssertions = function(response) {
expect(response.statusCode).equals(200);
expect(response.body.refId.length == 36);
expect(response.body.this1.length = 1);
expect(response.body.that2.length = 2);
expect(response.body.that3.length = 3);
};
describe('my test 1', function() {
it('response with email id reference expected', function(done) {
request
.post(messagingApiPath)
.send(input)
.expect((response) => positiveAssertions(response))
.end(done);
});
});
I am making an API call through an Observable. If this API call takes more than 200ms, I would like to show a loading screen (by assigning 'true' to my 'loading' variable), otherwise I don't want to show anything, in order to avoid a blink on screen.
Is there an RxJS operator capable of doing this ?
this.apiService.get(`/api/someEndpoint`)
// I hope for something like
.triggerIfAtLeastThisAmountOfTimeHasElapsed(200, () => {
this.loading = true;
})
.subscribe(response => {
// Process the response
this.loading = false;
});
There are many ways to do this so you can use for example this:
const api = this.apiService.get(`/api/someEndpoint`);
const loading = Observable
.timer(1000)
.do(() => loading = true) // show loading
.ignoreElements(); // or `filter(() => false)
Observable.merge(api, loading)
.take(1)
.subscribe(() => loading = false);
Along the same lines of Martin's response, this is an example that should simulate your context
const obs1 = Observable.timer(200).take(1);
const apiSubject = new Subject<string>();
const apiObs = apiSubject.asObservable();
const apiExecutionElapsed = 1000;
const obs3 = Observable.merge(obs1, apiObs);
let loading = undefined;
obs3.subscribe(
data => {
console.log(data);
if (loading === undefined && data === 0) {
loading = true;
} else {
loading = false;
}
console.log('loading', loading);
},
console.error,
() => {
loading = false;
console.log('loading', loading);
}
)
setTimeout(() => {
apiSubject.next('I am the result of the API');
apiSubject.complete()}, apiExecutionElapsed)
If the execution of the api (apiExecutionElapsed) takes longer than the configured timer (200 ms in this case) you see the loading flag to become first true and then false. Otherwise it remains always false.
I have a AJAX request that's invoked in an interval of every 10 seconds, but I want to be able to call the AJAX only after my previous request is completed.
$interval(function () {
$scope.getContent(2);
}, 10000);
With the above code, the ajax request is performed every 10 seconds irrespective of my previous request being completed. How can I achieve that?
Is this what you are trying to achieve:
DEMO
app.js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $interval, $http, $timeout) {
// create variable to store interval promise
var interval;
$scope.callCount = 0;
$scope.pending = false
$scope.start = start;
$scope.stop = stop;
$scope.getContent = getContent;
function getContent(){
$scope.pending = true;
$scope.callCount += 1;
return $http
.get('foo.json')
.then(function(response){
console.log('response', response);
$scope.pending = false;
// call stop() if you don't want to
// continue calling
// call stop() then start()
// if you want to call again
// immediately and continue calling
// every 10 seconds
})
.catch(function(){
$scope.pending = false;
});
// comment out the $http call above
// and uncomment the $timeout code
// below to test that start() does not
// call getContent() if pending
// still true
// $scope.pending = true;
// $scope.callCount += 1;
// return $timeout(function(){
// $scope.pending = false;
// }, 11000)
}
function start(){
if(angular.isDefined(interval)) return;
$scope.getContent();
interval = $interval(function() {
if(!$scope.pending){
$scope.getContent();
}
}, 10000);
}
function stop(){
if(angular.isDefined(interval)) {
console.log('stopping')
$interval.cancel(interval);
interval = undefined;
}
}
});
html
<body ng-controller="MainCtrl">
<p>Call Count: {{callCount}}</p>
<span ng-show="pending">pending...</span>
<button ng-click="start()">Start</button>
<button ng-click="stop()">Stop</button>
</body>
$interval(function () {
if($scope.flag){
$scope.getContent(2);
$scope.flag=false;
}
}, 10000);
and set $scope.flag value to true when your previous request is completed(i.e in callback).
I'm trying to detect if the mousedown event is held for a period of time before a mouseup.
I'm using timeout() on an Observable created with fromEvent() to do so, but the timeout returns both Observables.
Below, subscribing to stream returns the event if mousedown is triggered within 1 second, but it also returns 1.
var mousedown = Rx.Observable.fromEvent(target, 'mousedown');
var stream = mousedown.timeout(1000, Rx.Observable.return(1));
var sub = stream.subscribe(
function (x) {
console.log('Next: '+x);
},
function (err) {
console.log('Err: '+err);
},
function () {
console.log('Complete');
}
);
However, this works as expected:
var source = Rx.Observable.return(42)
.delay(200)
.timeout(1000, Rx.Observable.return(1));
I'd like this code to work:
var mousedown = Rx.Observable.fromEvent(target, 'mousedown');
var mouseup = Rx.Observable.fromEvent(target, 'mouseup');
var clickhold = mousedown
.flatMap(function (){
return mouseup.timeout(1000, Rx.Observable.return('hold'));
})
.filter(function (x) {
return x === 'hold';
});
clickhold.subscribe(
function (x) {
console.log('Next: '+x);
},
function (err) {
console.log('Err: '+err);
},
function () {
console.log('Complete');
}
);
Instead of using timeout, I used delay and takeUntil:
var target,
mousedown,
mouseup;
target = document.querySelector('input');
mousedown = Rx.Observable.fromEvent(target, 'mousedown');
mouseup = Rx.Observable.fromEvent(target, 'mouseup');
var clickhold = mousedown
.flatMap(function(){
// Triggered instantly after mousedown event.
return Rx.Observable
.return('hold')
.delay(1000)
// Discards the return value if by the time .delay() is complete
// mouseup event has been already completed.
.takeUntil(mouseup);
});
clickhold.subscribe(
function (x) {
console.log('Next: ' + x);
},
function (err) {
console.log('Err: ' + err);
},
function () {
console.log('Complete');
}
);
<script src='https://rawgit.com/Reactive-Extensions/RxJS/v.2.5.3/dist/rx.all.js'></script>
<input type='button' value='Button' />
You came up with a great solution on your own. Here's what I would change:
Move the inner observable (timer(...).takeUntil(...).select(...)) out of flatMap, so it isn't re-allocated for each mouse down.
You've got the rest right. For my usage, I usually retain the original mousedown event and use that instead of 'hold'. That requires returnValue and delay instead of timer and select.
var target,
mousedown,
mouseup;
target = document.querySelector('input');
mousedown = Rx.Observable.fromEvent(target, 'mousedown');
mouseup = Rx.Observable.fromEvent(target, 'mouseup');
var clickhold = mousedown
.flatMap(function (e) {
return Rx.Observable
.return(e)
.delay(1000)
.takeUntil(mouseup);
});
clickhold.subscribe(function (x) {
console.log('onNext: ', x);
});
<script src='https://rawgit.com/Reactive-Extensions/RxJS/v.2.5.3/dist/rx.all.js'></script>
<input type='button' value='Button' />
Or, for a completely different approach...
var Observable = Rx.Observable,
fromEvent = Observable.fromEvent.bind(Observable, target),
holdDelay = Observable.empty().delay(1000);
Observable
.merge(
[
fromEvent('mouseup')
.map(empty),
fromEvent('mousedown')
.map(Observable.returnValue)
.map(holdDelay.concat.bind(holdDelay))
]
)
.switchLatest();
Ok so that's weird. I'm really just giving it as food for though, and to show off that this can be done in a number of different ways.
i have been trying to integrate jquery-idle-timeout-plugin to show the session time out popup warning when user being idle for a period of time.
Everything works properly apart from new tab of browser. when i move to new tab or other tab then or have any post back then the session become alive and re-set the timer for that particular tab but doesn't re-set timer for other tabs or pages.
please have a look at the following code
<script type="text/javascript">
var idleTime = 12000; // number of miliseconds until the user is considered idle
var initialSessionTimeoutMessage = 'Your session will expire in <span id="sessionTimeoutCountdown"></span> seconds.<br/><br />Click on <b>OK</b> to continue your session.';
var sessionTimeoutCountdownId = 'sessionTimeoutCountdown';
var redirectAfter = 10; // number of seconds to wait before redirecting the user
var path = getPath();
var redirectTo = "logout.aspx";
var keepAliveURL = 'Default.aspx'; // URL to call to keep the session alive
var expiredMessage = 'Your session has expired. You are being logged out for security reasons.'; // message to show user when the countdown reaches 0
var running = false; // var to check if the countdown is running
var timer; // reference to the setInterval timer so it can be stopped
$(document).ready(function () {
var path = window.location.pathname;
var file = path.split('/')[1];
if (file == 'Login.aspx') {
return;
}
// create the warning window and set autoOpen to false
var sessionTimeoutWarningDialog = $("#sessionTimeoutWarning");
$(sessionTimeoutWarningDialog).html(initialSessionTimeoutMessage);
$(sessionTimeoutWarningDialog).dialog({
title: 'Session Expiration Warning',
autoOpen: false, // set this to false so we can manually open it
closeOnEscape: false,
draggable: false,
width: 460,
minHeight: 50,
modal: true,
beforeclose: function () { // bind to beforeclose so if the user clicks on the "X" or escape to close the dialog, it will work too
// stop the timer
clearInterval(timer);
// stop countdown
running = false;
// ajax call to keep the server-side session alive
$.ajax({
url: keepAliveURL,
async: true
});
},
buttons: {
OK: function () {
// close dialog
$(this).dialog('close');
}
},
resizable: false,
open: function () {
// scrollbar fix for IE
$('body').css('overflow', 'hidden');
},
close: function () {
// reset overflow
$('body').css('overflow', 'auto');
}
}); // end of dialog
// start the idle timer
$.idleTimer(idleTime);
// bind to idleTimer's idle.idleTimer event
$(document).bind("idle.idleTimer", function () {
// if the user is idle and a countdown isn't already running
if ($.data(document, 'idleTimer') === 'idle' && !running) {
var counter = redirectAfter;
running = true;
// intialisze timer
$('#' + sessionTimeoutCountdownId).html(redirectAfter);
// open dialog
$(sessionTimeoutWarningDialog).dialog('open');
// create a timer that runs every second
timer = setInterval(function () {
counter -= 1;
// if the counter is 0, redirect the user
if (counter == 0) {
$(sessionTimeoutWarningDialog).html(expiredMessage);
$(sessionTimeoutWarningDialog).dialog('disable');
window.location = redirectTo;
} else {
$('#' + sessionTimeoutCountdownId).html(counter);
};
}, 1000);
};
});
});
</script>
please advice me that how to make functional the issue for other tabs (Sync the timer for all pages)
Thanks
Your idle timer can communicate (stay in sync) across multiple windows & tabs using localStorage variables. On github, marcuswestin/store.js provides good functionality with multiple browser varieties. Note that some browsers/devices don't (yet?) support localStorage, but most modern browsers do.
Here is the 'testing' code for an idleTimer plugin which provides synchronized windows & tabs, provided they are all within the same domain. It sets 2 localStorage variables, idleTimerLastActivity & idleTimerLoggedOut, to track the 'state' of the user's session.
Demo page. Open multiple windows/tabs and you can see how it works.
http://jillelaine.github.io/jquery-idleTimeout/
/**
* This work is licensed under the MIT License
*
* Configurable idle (no activity) timer and logout redirect for jQuery.
* Works across multiple windows, tabs and iframes from the same domain.
*
* Dependencies: JQuery v1.7+, JQuery UI, store.js from https://github.com/marcuswestin/store.js - v1.3.4+
*
* Commented and console logged for debugging with Firefox & Firebug or similar
* version 1.0.6
**/
/*global jQuery: false, document: false, store: false, clearInterval: false, setInterval: false, setTimeout: false, window: false, alert: false, console: false*/
/*jslint indent: 2, sloppy: true, plusplus: true*/
(function ($) {
$.fn.idleTimeout = function (options) {
console.log('start');
//##############################
//## Configuration Variables
//##############################
var defaults = {
idleTimeLimit: 30000, // 30 seconds for testing. 'No activity' time limit in milliseconds. 1200000 = 20 Minutes
dialogDisplayLimit: 20000, // 20 seconds for testing. Time to display the warning dialog before redirect (and optional callback) in milliseconds. 180000 = 3 Minutes
redirectUrl: '/logout', // redirect to this url on timeout logout. Set to "redirectUrl: false" to disable redirect
// optional custom callback to perform before redirect
customCallback: false, // set to false for no customCallback
// customCallback: function () { // define optional custom js function
// perform custom action before logout
// },
// configure which activity events to detect
// http://www.quirksmode.org/dom/events/
// https://developer.mozilla.org/en-US/docs/Web/Reference/Events
activityEvents: 'click keypress scroll wheel mousewheel', // separate each event with a space
//dialog box configuration
dialogTitle: 'Session Expiration Warning',
dialogText: 'Because you have been inactive, your session is about to expire.',
// server-side session keep-alive timer
sessionKeepAliveTimer: 600000 // Ping the server at this interval in milliseconds. 600000 = 10 Minutes
// sessionKeepAliveTimer: false // Set to false to disable pings
},
//##############################
//## Private Variables
//##############################
opts = $.extend(defaults, options),
checkHeartbeat = 2000, // frequency to check for timeouts - 2000 = 2 seconds
origTitle = document.title, // save original browser title
sessionKeepAliveUrl = window.location.href, // set URL to ping to user's current window
keepSessionAlive, activityDetector,
idleTimer, remainingTimer, checkIdleTimeout, idleTimerLastActivity, startIdleTimer, stopIdleTimer,
openWarningDialog, dialogTimer, checkDialogTimeout, startDialogTimer, stopDialogTimer, isDialogOpen, destroyWarningDialog,
countdownDisplay, logoutUser,
checkForIframes, includeIframes, attachEventIframe; // iframe functionality
//##############################
//## Private Functions
//##############################
keepSessionAlive = function () {
if (opts.sessionKeepAliveTimer) {
var keepSession = function () {
if (idleTimerLastActivity === store.get('idleTimerLastActivity')) {
console.log('keep session alive function');
$.get(sessionKeepAliveUrl);
}
};
setInterval(keepSession, opts.sessionKeepAliveTimer);
}
};
activityDetector = function () {
$('body').on(opts.activityEvents, function () {
if (isDialogOpen() !== true) {
console.log('activity detected');
startIdleTimer();
} else {
console.log('dialog open. activity ignored');
}
});
};
checkIdleTimeout = function () {
var timeNow = $.now(), timeIdleTimeout = (store.get('idleTimerLastActivity') + opts.idleTimeLimit);
if (timeNow > timeIdleTimeout) {
console.log('timeNow: ' + timeNow + ' > idle ' + timeIdleTimeout);
if (isDialogOpen() !== true) {
console.log('dialog is not open & will be opened');
openWarningDialog();
startDialogTimer();
}
} else if (store.get('idleTimerLoggedOut') === true) { //a 'manual' user logout?
logoutUser();
} else {
console.log('idle not yet timed out');
if (isDialogOpen() === true) {
console.log('dialog is open & will be closed');
destroyWarningDialog();
stopDialogTimer();
}
}
};
startIdleTimer = function () {
stopIdleTimer();
idleTimerLastActivity = $.now();
store.set('idleTimerLastActivity', idleTimerLastActivity);
console.log('start idle timer: ' + idleTimerLastActivity);
idleTimer = setInterval(checkIdleTimeout, checkHeartbeat);
};
stopIdleTimer = function () {
clearInterval(idleTimer);
};
openWarningDialog = function () {
var dialogContent = "<div id='idletimer_warning_dialog'><p>" + opts.dialogText + "</p><p style='display:inline'>Time remaining: <div style='display:inline' id='countdownDisplay'></div></p></div>";
$(dialogContent).dialog({
buttons: {
"Stay Logged In": function () {
console.log('Stay Logged In button clicked');
destroyWarningDialog();
stopDialogTimer();
startIdleTimer();
},
"Log Out Now": function () {
console.log('Log Out Now button clicked');
logoutUser();
}
},
closeOnEscape: false,
modal: true,
title: opts.dialogTitle
});
// hide the dialog's upper right corner "x" close button
$('.ui-dialog-titlebar-close').css('display', 'none');
// start the countdown display
countdownDisplay();
// change title bar to warning message
document.title = opts.dialogTitle;
};
checkDialogTimeout = function () {
var timeNow = $.now(), timeDialogTimeout = (store.get('idleTimerLastActivity') + opts.idleTimeLimit + opts.dialogDisplayLimit);
if ((timeNow > timeDialogTimeout) || (store.get('idleTimerLoggedOut') === true)) {
console.log('timeNow: ' + timeNow + ' > dialog' + timeDialogTimeout);
logoutUser();
} else {
console.log('dialog not yet timed out');
}
};
startDialogTimer = function () {
dialogTimer = setInterval(checkDialogTimeout, checkHeartbeat);
};
stopDialogTimer = function () {
clearInterval(dialogTimer);
clearInterval(remainingTimer);
};
isDialogOpen = function () {
var dialogOpen = $("#idletimer_warning_dialog").is(":visible");
if (dialogOpen === true) {
return true;
}
return false;
};
destroyWarningDialog = function () {
console.log('dialog destroyed');
$(".ui-dialog-content").dialog('destroy').remove();
document.title = origTitle;
};
// display remaining time on warning dialog
countdownDisplay = function () {
var dialogDisplaySeconds = opts.dialogDisplayLimit / 1000, mins, secs;
remainingTimer = setInterval(function () {
mins = Math.floor(dialogDisplaySeconds / 60); // minutes
if (mins < 10) { mins = '0' + mins; }
secs = dialogDisplaySeconds - (mins * 60); // seconds
if (secs < 10) { secs = '0' + secs; }
$('#countdownDisplay').html(mins + ':' + secs);
dialogDisplaySeconds -= 1;
}, 1000);
};
logoutUser = function () {
console.log('logout function');
store.set('idleTimerLoggedOut', true);
if (opts.customCallback) {
console.log('logout function custom callback');
opts.customCallback();
}
if (opts.redirectUrl) {
console.log('logout function redirect to URL');
window.location.href = opts.redirectUrl;
}
};
// document must be in readyState 'complete' before looking for iframes
checkForIframes = function () {
var docReadyCheck, isDocReady;
docReadyCheck = function () {
if (document.readyState === "complete") {
console.log('check for iframes, now that the document is complete');
clearInterval(isDocReady);
includeIframes();
}
};
isDocReady = setInterval(docReadyCheck, 1000);
};
// look for iframes
includeIframes = function () {
console.log('include iframes start');
var foundIframes = document.getElementsByTagName('iframe'), index, iframeItem;
if (foundIframes.length > 0) { //at least one iframe found
console.log('iframes found: ' + foundIframes.length);
// attach events to each iframe found
for (index = 0; index < foundIframes.length; index++) {
iframeItem = foundIframes.item(index);
if (iframeItem.attachEvent) { // IE < 11. Returns a boolean true/false
console.log('attach event to iframe. Browser IE < 11');
iframeItem.attachEvent('onload', attachEventIframe(index));
} else { // IE >= 11 and FF, etc.
console.log('attach event to iframe. Browser NOT IE < 11');
iframeItem.addEventListener('load', attachEventIframe(index), false);
}
} // end for loop
} // end if any iframes
};
// attach events to each iframe
attachEventIframe = function (index) {
var iframe = $('iframe:eq(' + index + ')').contents().find('html');
iframe.on(opts.activityEvents, function (event) {
console.log('bubbling iframe activity event to body of page');
$('body').trigger(event);
});
};
//###############################
// Build & Return the instance of the item as a plugin
// This is your construct.
//###############################
return this.each(function () {
if (store.enabled) {
idleTimerLastActivity = $.now();
store.set('idleTimerLastActivity', idleTimerLastActivity);
store.set('idleTimerLoggedOut', false);
} else {
alert('Please disable "Private Mode", or upgrade to a modern browser. Or perhaps a dependent file missing. Please see: https://github.com/marcuswestin/store.js');
}
activityDetector();
keepSessionAlive();
startIdleTimer();
checkForIframes();
});
};
}(jQuery));
I got your point, Idle timeout plugin using jQuery is just identify the timeout its not integrated with multiple tab or window options. But you can just achieve that in a tricky way like below.
jQuery(window).blur(function(){
//your code here to stop the finding idle time check
});
jQuery(window).focus(function(){
//your code here to start idle time check
});
See:http://www.walkswithme.net/idle-timeout-plugin-using-jquery