From 41b6d4b702e8ed32f7ea51edffd0005639f77138 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 24 Jan 2014 12:44:31 +0100 Subject: Added OC.buidQueryString() utility function Makes it possible to create query strings by passing a JavaScript hash map and automatically encodes the keys and values. --- core/js/js.js | 28 ++++++++++++++++++++++++++++ core/js/tests/specs/coreSpec.js | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) (limited to 'core/js') diff --git a/core/js/js.js b/core/js/js.js index e84f482d672..976027dd06b 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -364,6 +364,34 @@ var OC={ } return result; }, + + /** + * Builds a URL query from a JS map. + * @param params parameter map + * @return string containing a URL query (without question) mark + */ + buildQueryString: function(params) { + var s = ''; + var first = true; + if (!params) { + return s; + } + for (var key in params) { + var value = params[key]; + if (first) { + first = false; + } + else { + s += '&'; + } + s += encodeURIComponent(key); + if (value !== null && typeof(value) !== 'undefined') { + s += '=' + encodeURIComponent(value); + } + } + return s; + }, + /** * Opens a popup with the setting for an app. * @param appid String. The ID of the app e.g. 'calendar', 'contacts' or 'files'. diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js index 827669f270b..28c20a0642e 100644 --- a/core/js/tests/specs/coreSpec.js +++ b/core/js/tests/specs/coreSpec.js @@ -67,4 +67,41 @@ describe('Core base tests', function() { }); }); }); + describe('Query string building', function() { + it('Returns empty string when empty params', function() { + expect(OC.buildQueryString()).toEqual(''); + expect(OC.buildQueryString({})).toEqual(''); + }); + it('Encodes regular query strings', function() { + expect(OC.buildQueryString({ + a: 'abc', + b: 'def' + })).toEqual('a=abc&b=def'); + }); + it('Encodes special characters', function() { + expect(OC.buildQueryString({ + unicode: '汉字', + })).toEqual('unicode=%E6%B1%89%E5%AD%97'); + expect(OC.buildQueryString({ + b: 'spaace value', + 'space key': 'normalvalue', + 'slash/this': 'amp&ersand' + })).toEqual('b=spaace%20value&space%20key=normalvalue&slash%2Fthis=amp%26ersand'); + }); + it('Encodes data types and empty values', function() { + expect(OC.buildQueryString({ + 'keywithemptystring': '', + 'keywithnull': null, + 'keywithundefined': null, + something: 'else' + })).toEqual('keywithemptystring=&keywithnull&keywithundefined&something=else'); + expect(OC.buildQueryString({ + 'booleanfalse': false, + 'booleantrue': true + })).toEqual('booleanfalse=false&booleantrue=true'); + expect(OC.buildQueryString({ + 'number': 123, + })).toEqual('number=123'); + }); + }); }); -- cgit v1.2.3 From c6695bbd764be9f43067c09894e36422c2b92b49 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 24 Jan 2014 13:32:31 +0100 Subject: Fixed download URL in public page - Refactored download URL building to make it overridable - Added download URL override in public page - Added JS unit tests for download URL - Added OC.redirect() method to facilitate unit testing --- apps/files/js/fileactions.js | 5 ++- apps/files/js/filelist.js | 14 ++++++++ apps/files/tests/js/fileactionsSpec.js | 61 ++++++++++++++++++++++++++++++++++ apps/files/tests/js/filelistSpec.js | 6 ++++ apps/files_sharing/js/public.js | 16 ++++----- core/js/js.js | 6 ++++ 6 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 apps/files/tests/js/fileactionsSpec.js (limited to 'core/js') diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 74bb711ef3d..eb59e71a030 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -173,7 +173,10 @@ $(document).ready(function () { FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () { return OC.imagePath('core', 'actions/download'); }, function (filename) { - window.location = OC.filePath('files', 'ajax', 'download.php') + '?files=' + encodeURIComponent(filename) + '&dir=' + encodeURIComponent($('#dir').val()); + var url = FileList.getDownloadUrl(filename); + if (url) { + OC.redirect(url); + } }); } $('#fileList tr').each(function () { diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 66968ab54c7..63fd0f4ce05 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -780,6 +780,20 @@ var FileList={ $('#fileList tr.searchresult').each(function(i,e) { $(e).removeClass("searchresult"); }); + }, + + /** + * Returns the download URL of the given file + * @param filename file name of the file + * @param dir optional directory in which the file name is, defaults to the current directory + */ + getDownloadUrl: function(filename, dir) { + var params = { + files: filename, + dir: dir || FileList.getCurrentDirectory(), + download: null + }; + return OC.filePath('files', 'ajax', 'download.php') + '?' + OC.buildQueryString(params); } }; diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js new file mode 100644 index 00000000000..23f7b58dcd9 --- /dev/null +++ b/apps/files/tests/js/fileactionsSpec.js @@ -0,0 +1,61 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2014 Vincent Petry +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see . +* +*/ +describe('FileActions tests', function() { + beforeEach(function() { + // init horrible parameters + var $body = $('body'); + $body.append(''); + $body.append(''); + // dummy files table + $filesTable = $body.append('
'); + }); + afterEach(function() { + $('#dir, #permissions, #filestable').remove(); + }); + it('calling display() sets file actions', function() { + // note: download_url is actually the link target, not the actual download URL... + var $tr = FileList.addFile('testName.txt', 1234, new Date(), false, false, {download_url: 'test/download/url'}); + + // no actions before call + expect($tr.find('.action[data-action=Download]').length).toEqual(0); + expect($tr.find('.action[data-action=Rename]').length).toEqual(0); + expect($tr.find('.action.delete').length).toEqual(0); + + FileActions.display($tr.find('td.filename'), true); + + // actions defined after cal + expect($tr.find('.action[data-action=Download]').length).toEqual(1); + expect($tr.find('.action[data-action=Rename]').length).toEqual(1); + expect($tr.find('.action.delete').length).toEqual(1); + }); + it('redirects to download URL when clicking download', function() { + var redirectStub = sinon.stub(OC, 'redirect'); + // note: download_url is actually the link target, not the actual download URL... + var $tr = FileList.addFile('test download File.txt', 1234, new Date(), false, false, {download_url: 'test/download/url'}); + FileActions.display($tr.find('td.filename'), true); + + $tr.find('.action[data-action=Download]').click(); + + expect(redirectStub.calledOnce).toEqual(true); + expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?files=test%20download%20File.txt&dir=%2Fsubdir&download'); + redirectStub.restore(); + }); +}); diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index be848e0e0b0..61e026c0725 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -32,10 +32,12 @@ describe('FileList tests', function() { }); it('generates file element with correct attributes when calling addFile', function() { var lastMod = new Date(10000); + // note: download_url is actually the link target, not the actual download URL... var $tr = FileList.addFile('testName.txt', 1234, lastMod, false, false, {download_url: 'test/download/url'}); expect($tr).toBeDefined(); expect($tr[0].tagName.toLowerCase()).toEqual('tr'); + expect($tr.find('a:first').attr('href')).toEqual('test/download/url'); expect($tr.attr('data-type')).toEqual('file'); expect($tr.attr('data-file')).toEqual('testName.txt'); expect($tr.attr('data-size')).toEqual('1234'); @@ -54,4 +56,8 @@ describe('FileList tests', function() { expect($tr.attr('data-permissions')).toEqual('31'); //expect($tr.attr('data-mime')).toEqual('httpd/unix-directory'); }); + it('returns correct download URL', function() { + expect(FileList.getDownloadUrl('some file.txt')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?files=some%20file.txt&dir=%2Fsubdir&download'); + expect(FileList.getDownloadUrl('some file.txt', '/anotherpath/abc')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?files=some%20file.txt&dir=%2Fanotherpath%2Fabc&download'); + }); }); diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 4c0b0ad9d48..79c15623c0c 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -34,18 +34,16 @@ $(document).ready(function() { window.location = $(tr).find('a.name').attr('href'); } }); - FileActions.register('file', 'Download', OC.PERMISSION_READ, '', function(filename) { - var tr = FileList.findFileEl(filename); - if (tr.length > 0) { - window.location = $(tr).find('a.name').attr('href'); - } - }); - FileActions.register('dir', 'Download', OC.PERMISSION_READ, '', function(filename) { + + // override since the format is different + FileList.getDownloadUrl = function(filename, dir) { + // we use this because we need the service and token attributes var tr = FileList.findFileEl(filename); if (tr.length > 0) { - window.location = $(tr).find('a.name').attr('href')+'&download'; + return $(tr).find('a.name').attr('href') + '&download'; } - }); + return null; + }; } var file_upload_start = $('#file_upload_start'); diff --git a/core/js/js.js b/core/js/js.js index 976027dd06b..1c7d89ea055 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -252,6 +252,12 @@ var OC={ } return link; }, + /** + * Redirect to the target URL, can also be used for downloads. + */ + redirect: function(targetUrl) { + window.location = targetUrl; + }, /** * get the absolute path to an image file * @param app the app id to which the image belongs -- cgit v1.2.3 From f7ac9f8069ea5e8ad8eb5c0f899f6ac8e39ef07f Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 27 Jan 2014 21:15:21 +0100 Subject: Fixed unit test scripts + coverage Tried to add more apps (others break). "preprocessors" is now populated automatically based on the tested apps. --- .gitignore | 1 + autotest-js.sh | 2 +- core/js/core.json | 43 +++++++++++++++------------------- tests/karma.config.js | 64 +++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 73 insertions(+), 37 deletions(-) (limited to 'core/js') diff --git a/.gitignore b/.gitignore index 8c8b61d701b..25cb1b227f9 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,7 @@ nbproject # Tests - auto-generated files /data-autotest /tests/coverage* +/tests/karma-coverage /tests/autoconfig* /tests/autotest* /tests/data/lorem-copy.txt diff --git a/autotest-js.sh b/autotest-js.sh index 78f4948e7ad..8b9a106b021 100755 --- a/autotest-js.sh +++ b/autotest-js.sh @@ -33,5 +33,5 @@ then exit 2 fi -KARMA_TESTSUITE="$1" $KARMA start tests/karma.config.js --single-run +NODE_PATH='build/node_modules' KARMA_TESTSUITE="$1" $KARMA start tests/karma.config.js --single-run diff --git a/core/js/core.json b/core/js/core.json index 79cfc42f587..4beab7cf796 100644 --- a/core/js/core.json +++ b/core/js/core.json @@ -1,28 +1,23 @@ { + "libraries": [ + "jquery-1.10.0.min.js", + "jquery-migrate-1.2.1.min.js", + "jquery-ui-1.10.0.custom.js", + "jquery-showpassword.js", + "jquery.infieldlabel.js", + "jquery.placeholder.js", + "jquery-tipsy.js" + ], "modules": [ - "jquery-1.10.0.min.js", - "jquery-migrate-1.2.1.min.js", - "jquery-ui-1.10.0.custom.js", - "jquery-showpassword.js", - "jquery.infieldlabel.js", - "jquery.placeholder.js", - "jquery-tipsy.js", - "compatibility.js", - "jquery.ocdialog.js", - "oc-dialogs.js", - "js.js", - "octemplate.js", - "eventsource.js", - "config.js", - "multiselect.js", - "search.js", - "router.js", - "oc-requesttoken.js", - "styles.js", - "apps.js", - "fixes.js", - "jquery-ui-2.10.0.custom.js", - "jquery-tipsy.js", - "jquery.ocdialog.js" + "compatibility.js", + "jquery.ocdialog.js", + "oc-dialogs.js", + "js.js", + "octemplate.js", + "eventsource.js", + "config.js", + "multiselect.js", + "router.js", + "oc-requesttoken.js" ] } diff --git a/tests/karma.config.js b/tests/karma.config.js index f73ade0f3c6..529bd31338f 100644 --- a/tests/karma.config.js +++ b/tests/karma.config.js @@ -29,25 +29,52 @@ * environment variable to the apps name, for example "core" or "files_encryption". * Multiple apps can be specified by separating them with space. * + * Setting the environment variable NOCOVERAGE to 1 will disable the coverage + * preprocessor, which is needed to be able to debug tests properly in a browser. */ + +/* jshint node: true */ module.exports = function(config) { + function findApps() { + /* + var fs = require('fs'); + var apps = fs.readdirSync('apps'); + return apps; + */ + // other apps tests don't run yet... needs further research / clean up + return ['files']; + } + + // respect NOCOVERAGE env variable + // it is useful to disable coverage for debugging + // because the coverage preprocessor will wrap the JS files somehow + var enableCoverage = !parseInt(process.env.NOCOVERAGE, 10); + console.log('Coverage preprocessor: ', enableCoverage?'enabled':'disabled'); + // default apps to test when none is specified (TODO: read from filesystem ?) - var defaultApps = 'core files'; - var appsToTest = process.env.KARMA_TESTSUITE || defaultApps; + var appsToTest = process.env.KARMA_TESTSUITE; + if (appsToTest) { + appsToTest = appsToTest.split(' '); + } + else { + appsToTest = ['core'].concat(findApps()); + } + + console.log('Apps to test: ', appsToTest); // read core files from core.json, // these are required by all apps so always need to be loaded // note that the loading order is important that's why they // are specified in a separate file var corePath = 'core/js/'; - var coreFiles = require('../' + corePath + 'core.json').modules; + var coreModule = require('../' + corePath + 'core.json'); var testCore = false; var files = []; var index; + var preprocessors = {}; // find out what apps to test from appsToTest - appsToTest = appsToTest.split(' '); index = appsToTest.indexOf('core'); if (index > -1) { appsToTest.splice(index, 1); @@ -60,11 +87,23 @@ module.exports = function(config) { // core mocks files.push(corePath + 'tests/specHelper.js'); - // add core files - for ( var i = 0; i < coreFiles.length; i++ ) { - files.push( corePath + coreFiles[i] ); + // add core library files + for ( var i = 0; i < coreModule.libraries.length; i++ ) { + var srcFile = corePath + coreModule.libraries[i]; + files.push(srcFile); + } + + // add core modules files + for ( var i = 0; i < coreModule.modules.length; i++ ) { + var srcFile = corePath + coreModule.modules[i]; + files.push(srcFile); + if (enableCoverage) { + preprocessors[srcFile] = 'coverage'; + } } + // TODO: settings pages + // need to test the core app as well ? if (testCore) { // core tests @@ -73,7 +112,11 @@ module.exports = function(config) { for ( var i = 0; i < appsToTest.length; i++ ) { // add app JS - files.push('apps/' + appsToTest[i] + '/js/*.js'); + var srcFile = 'apps/' + appsToTest[i] + '/js/*.js'; + files.push(srcFile); + if (enableCoverage) { + preprocessors[srcFile] = 'coverage'; + } // add test specs files.push('apps/' + appsToTest[i] + '/tests/js/*.js'); } @@ -83,7 +126,6 @@ module.exports = function(config) { // base path, that will be used to resolve files and exclude basePath: '..', - // frameworks to use frameworks: ['jasmine'], @@ -106,9 +148,7 @@ module.exports = function(config) { // web server port port: 9876, - preprocessors: { - 'apps/files/js/*.js': 'coverage' - }, + preprocessors: preprocessors, coverageReporter: { dir:'tests/karma-coverage', -- cgit v1.2.3 From 63cca35baa14727cdf71f2b109523c28d16f1c58 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 30 Jan 2014 13:22:16 +0100 Subject: Added core unit tests for basename and dirname Note that it doesn't work 100% like the PHP functions so the tests have TODO comments to fix those core functions eventually. --- core/js/tests/specs/coreSpec.js | 99 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) (limited to 'core/js') diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js index 827669f270b..373685d87a5 100644 --- a/core/js/tests/specs/coreSpec.js +++ b/core/js/tests/specs/coreSpec.js @@ -18,6 +18,8 @@ * License along with this library. If not, see . * */ + +/* global OC */ describe('Core base tests', function() { describe('Base values', function() { it('Sets webroots', function() { @@ -25,6 +27,103 @@ describe('Core base tests', function() { expect(OC.appswebroots).toBeDefined(); }); }); + describe('basename', function() { + it('Returns the nothing if no file name given', function() { + expect(OC.basename('')).toEqual(''); + }); + it('Returns the nothing if dir is root', function() { + expect(OC.basename('/')).toEqual(''); + }); + it('Returns the same name if no path given', function() { + expect(OC.basename('some name.txt')).toEqual('some name.txt'); + }); + it('Returns the base name if root path given', function() { + expect(OC.basename('/some name.txt')).toEqual('some name.txt'); + }); + it('Returns the base name if double root path given', function() { + expect(OC.basename('//some name.txt')).toEqual('some name.txt'); + }); + it('Returns the base name if subdir given without root', function() { + expect(OC.basename('subdir/some name.txt')).toEqual('some name.txt'); + }); + it('Returns the base name if subdir given with root', function() { + expect(OC.basename('/subdir/some name.txt')).toEqual('some name.txt'); + }); + it('Returns the base name if subdir given with double root', function() { + expect(OC.basename('//subdir/some name.txt')).toEqual('some name.txt'); + }); + it('Returns the base name if subdir has dot', function() { + expect(OC.basename('/subdir.dat/some name.txt')).toEqual('some name.txt'); + }); + it('Returns dot if file name is dot', function() { + expect(OC.basename('/subdir/.')).toEqual('.'); + }); + // TODO: fix the source to make it work like PHP's basename + it('Returns the dir itself if no file name given', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.basename('subdir/')).toEqual('subdir'); + expect(OC.basename('subdir/')).toEqual(''); + }); + it('Returns the dir itself if no file name given with root', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.basename('/subdir/')).toEqual('subdir'); + expect(OC.basename('/subdir/')).toEqual(''); + }); + }); + describe('dirname', function() { + it('Returns the nothing if no file name given', function() { + expect(OC.dirname('')).toEqual(''); + }); + it('Returns the root if dir is root', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.dirname('/')).toEqual('/'); + expect(OC.dirname('/')).toEqual(''); + }); + it('Returns the root if dir is double root', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.dirname('//')).toEqual('/'); + expect(OC.dirname('//')).toEqual('/'); // oh no... + }); + it('Returns dot if dir is dot', function() { + expect(OC.dirname('.')).toEqual('.'); + }); + it('Returns dot if no root given', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.dirname('some dir')).toEqual('.'); + expect(OC.dirname('some dir')).toEqual('some dir'); // oh no... + }); + it('Returns the dir name if file name and root path given', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.dirname('/some name.txt')).toEqual('/'); + expect(OC.dirname('/some name.txt')).toEqual(''); + }); + it('Returns the dir name if double root path given', function() { + expect(OC.dirname('//some name.txt')).toEqual('/'); // how lucky... + }); + it('Returns the dir name if subdir given without root', function() { + expect(OC.dirname('subdir/some name.txt')).toEqual('subdir'); + }); + it('Returns the dir name if subdir given with root', function() { + expect(OC.dirname('/subdir/some name.txt')).toEqual('/subdir'); + }); + it('Returns the dir name if subdir given with double root', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.dirname('//subdir/some name.txt')).toEqual('/subdir'); + expect(OC.dirname('//subdir/some name.txt')).toEqual('//subdir'); // oh... + }); + it('Returns the dir name if subdir has dot', function() { + expect(OC.dirname('/subdir.dat/some name.txt')).toEqual('/subdir.dat'); + }); + it('Returns the dir name if file name is dot', function() { + expect(OC.dirname('/subdir/.')).toEqual('/subdir'); + }); + it('Returns the dir name if no file name given', function() { + expect(OC.dirname('subdir/')).toEqual('subdir'); + }); + it('Returns the dir name if no file name given with root', function() { + expect(OC.dirname('/subdir/')).toEqual('/subdir'); + }); + }); describe('Link functions', function() { var TESTAPP = 'testapp'; var TESTAPP_ROOT = OC.webroot + '/appsx/testapp'; -- cgit v1.2.3 From 912da8d27756d9f0d55821338f6d8698af2dbd27 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 4 Feb 2014 13:56:10 +0100 Subject: Added session_keepalive setting When session_keepalive is true (default) the heartbeat will be send as often as the half of the session timeout value. --- config/config.sample.php | 7 +++++++ core/js/config.php | 6 ++++++ core/js/js.js | 53 ++++++++++++++++++++++++++++++++++-------------- 3 files changed, 51 insertions(+), 15 deletions(-) (limited to 'core/js') diff --git a/config/config.sample.php b/config/config.sample.php index 01abc583688..ef5fb7ea5a5 100755 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -184,6 +184,13 @@ $CONFIG = array( /* Life time of a session after inactivity */ "session_lifetime" => 60 * 60 * 24, +/* + * Enable/disable session keep alive when a user is logged in in the Web UI. + * This is achieved by sending a "heartbeat" to the server to prevent + * the session timing out. + */ +"session_keepalive" => true, + /* Custom CSP policy, changing this will overwrite the standard policy */ "custom_csp_policy" => "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src *; font-src 'self' data:; media-src *", diff --git a/core/js/config.php b/core/js/config.php index dd46f7889d1..517ea1615a8 100644 --- a/core/js/config.php +++ b/core/js/config.php @@ -55,6 +55,12 @@ $array = array( ) ), "firstDay" => json_encode($l->l('firstday', 'firstday')) , + "oc_config" => json_encode( + array( + 'session_lifetime' => \OCP\Config::getSystemValue('session_lifetime', 60 * 60 * 24), + 'session_keepalive' => \OCP\Config::getSystemValue('session_keepalive', true) + ) + ) ); // Echo it diff --git a/core/js/js.js b/core/js/js.js index 1c7d89ea055..cd9b8bd3010 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -11,6 +11,8 @@ var oc_webroot; var oc_current_user = document.getElementsByTagName('head')[0].getAttribute('data-user'); var oc_requesttoken = document.getElementsByTagName('head')[0].getAttribute('data-requesttoken'); +window.oc_config = window.oc_config || {}; + if (typeof oc_webroot === "undefined") { oc_webroot = location.pathname; var pos = oc_webroot.indexOf('/index.php/'); @@ -742,8 +744,39 @@ function fillWindow(selector) { console.warn("This function is deprecated! Use CSS instead"); } -$(document).ready(function(){ - sessionHeartBeat(); +/** + * Initializes core + */ +function initCore() { + + /** + * Calls the server periodically to ensure that session doesnt + * time out + */ + function initSessionHeartBeat(){ + // interval in seconds + var interval = 900; + if (oc_config.session_lifetime) { + interval = Math.floor(oc_config.session_lifetime / 2); + } + // minimum one minute + if (interval < 60) { + interval = 60; + } + OC.Router.registerLoadedCallback(function(){ + var url = OC.Router.generate('heartbeat'); + setInterval(function(){ + $.post(url); + }, interval * 1000); + }); + } + + // session heartbeat (defalts to enabled) + if (typeof(oc_config.session_keepalive) === 'undefined' || + !!oc_config.session_keepalive) { + + initSessionHeartBeat(); + } if(!SVGSupport()){ //replace all svg images with png images for browser that dont support svg replaceSVG(); @@ -856,7 +889,9 @@ $(document).ready(function(){ $('input[type=text]').focus(function(){ this.select(); }); -}); +} + +$(document).ready(initCore); /** * Filter Jquery selector by attribute value @@ -986,15 +1021,3 @@ jQuery.fn.exists = function(){ return this.length > 0; }; -/** - * Calls the server periodically every 15 mins to ensure that session doesnt - * time out - */ -function sessionHeartBeat(){ - OC.Router.registerLoadedCallback(function(){ - var url = OC.Router.generate('heartbeat'); - setInterval(function(){ - $.post(url); - }, 900000); - }); -} -- cgit v1.2.3 From e75f7e58e9778bbe5bed6ba387c781c2ece94bbf Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 4 Feb 2014 13:56:41 +0100 Subject: Added unit tests for session_keepalive / heartbeat --- core/js/tests/specHelper.js | 18 +++++++++- core/js/tests/specs/coreSpec.js | 74 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) (limited to 'core/js') diff --git a/core/js/tests/specHelper.js b/core/js/tests/specHelper.js index 4a30878df51..1848d08354e 100644 --- a/core/js/tests/specHelper.js +++ b/core/js/tests/specHelper.js @@ -19,6 +19,8 @@ * */ +/* global OC */ + /** * Simulate the variables that are normally set by PHP code */ @@ -57,10 +59,15 @@ window.oc_webroot = location.href + '/'; window.oc_appswebroots = { "files": window.oc_webroot + '/apps/files/' }; +window.oc_config = { + session_lifetime: 600 * 1000, + session_keepalive: false +}; // global setup for all tests (function setupTests() { - var fakeServer = null; + var fakeServer = null, + routesRequestStub; beforeEach(function() { // enforce fake XHR, tests should not depend on the server and @@ -78,9 +85,18 @@ window.oc_appswebroots = { // make it globally available, so that other tests can define // custom responses window.fakeServer = fakeServer; + + OC.Router.routes = []; + OC.Router.routes_request = { + state: sinon.stub().returns('resolved'), + done: sinon.stub() + }; }); afterEach(function() { + OC.Router.routes_request.state.reset(); + OC.Router.routes_request.done.reset(); + // uncomment this to log requests // console.log(window.fakeServer.requests); fakeServer.restore(); diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js index 28c20a0642e..18652d4177f 100644 --- a/core/js/tests/specs/coreSpec.js +++ b/core/js/tests/specs/coreSpec.js @@ -104,4 +104,78 @@ describe('Core base tests', function() { })).toEqual('number=123'); }); }); + describe('Session heartbeat', function() { + var clock, + oldConfig, + loadedStub, + routeStub, + counter; + + beforeEach(function() { + clock = sinon.useFakeTimers(); + oldConfig = window.oc_config; + loadedStub = sinon.stub(OC.Router, 'registerLoadedCallback'); + routeStub = sinon.stub(OC.Router, 'generate').returns('/heartbeat'); + counter = 0; + + fakeServer.autoRespond = true; + fakeServer.autoRespondAfter = 0; + fakeServer.respondWith(/\/heartbeat/, function(xhr) { + counter++; + xhr.respond(200, {'Content-Type': 'application/json'}, '{}'); + }); + }); + afterEach(function() { + clock.restore(); + window.oc_config = oldConfig; + loadedStub.restore(); + routeStub.restore(); + }); + it('sends heartbeat half the session lifetime when heartbeat enabled', function() { + window.oc_config = { + session_keepalive: true, + session_lifetime: 300 + }; + window.initCore(); + expect(loadedStub.calledOnce).toEqual(true); + loadedStub.yield(); + expect(routeStub.calledWith('heartbeat')).toEqual(true); + + expect(counter).toEqual(0); + + // less than half, still nothing + clock.tick(100 * 1000); + expect(counter).toEqual(0); + + // reach past half (160), one call + clock.tick(55 * 1000); + expect(counter).toEqual(1); + + // almost there to the next, still one + clock.tick(140 * 1000); + expect(counter).toEqual(1); + + // past it, second call + clock.tick(20 * 1000); + expect(counter).toEqual(2); + }); + it('does no send heartbeat when heartbeat disabled', function() { + window.oc_config = { + session_keepalive: false, + session_lifetime: 300 + }; + window.initCore(); + expect(loadedStub.notCalled).toEqual(true); + expect(routeStub.notCalled).toEqual(true); + + expect(counter).toEqual(0); + + clock.tick(1000000); + + // still nothing + expect(counter).toEqual(0); + }); + + }); }); + -- cgit v1.2.3 From 45ab9810c597939074f750f985decc6d56a38c97 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Tue, 4 Feb 2014 20:58:06 +0100 Subject: fixing typos --- core/js/js.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'core/js') diff --git a/core/js/js.js b/core/js/js.js index cd9b8bd3010..cb177712a3a 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -750,7 +750,7 @@ function fillWindow(selector) { function initCore() { /** - * Calls the server periodically to ensure that session doesnt + * Calls the server periodically to ensure that session doesn't * time out */ function initSessionHeartBeat(){ @@ -771,7 +771,7 @@ function initCore() { }); } - // session heartbeat (defalts to enabled) + // session heartbeat (defaults to enabled) if (typeof(oc_config.session_keepalive) === 'undefined' || !!oc_config.session_keepalive) { -- cgit v1.2.3