Added Javascript unit tests

- added karma utility to run jasmine unit tests
- added Sinon library (for stubs/mocks/fakeserver)
- added a few unit tests for core and files
- added autotest-js.sh script
This commit is contained in:
Vincent Petry 2014-01-12 18:57:53 +01:00
parent 85e00ad35a
commit 350214c609
11 changed files with 4812 additions and 2 deletions

View File

@ -0,0 +1,54 @@
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
describe('FileList tests', function() {
beforeEach(function() {
// init horrible parameters
$('<input type="hidden" id="dir" value="/subdir"></input>').append('body');
$('<input type="hidden" id="permissions" value="31"></input>').append('body');
});
afterEach(function() {
$('#dir, #permissions').remove();
});
it('generates file element with correct attributes when calling addFile', function() {
var lastMod = new Date(10000);
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.attr('data-type')).toEqual('file');
expect($tr.attr('data-file')).toEqual('testName.txt');
expect($tr.attr('data-size')).toEqual('1234');
//expect($tr.attr('data-permissions')).toEqual('31');
//expect($tr.attr('data-mime')).toEqual('plain/text');
});
it('generates dir element with correct attributes when calling addDir', function() {
var lastMod = new Date(10000);
var $tr = FileList.addDir('testFolder', 1234, lastMod, false);
expect($tr).toBeDefined();
expect($tr[0].tagName.toLowerCase()).toEqual('tr');
expect($tr.attr('data-type')).toEqual('dir');
expect($tr.attr('data-file')).toEqual('testFolder');
expect($tr.attr('data-size')).toEqual('1234');
//expect($tr.attr('data-permissions')).toEqual('31');
//expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
});
});

View File

@ -0,0 +1,81 @@
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
describe('Files tests', function() {
describe('File name validation', function() {
it('Validates correct file names', function() {
var fileNames = [
'boringname',
'something.with.extension',
'now with spaces',
'.a',
'..a',
'.dotfile',
'single\'quote',
' spaces before',
'spaces after ',
'allowed chars including the crazy ones $%&_-^@!,()[]{}=;#',
'汉字也能用',
'und Ümläüte sind auch willkommen'
];
for ( var i = 0; i < fileNames.length; i++ ) {
try {
expect(Files.isFileNameValid(fileNames[i])).toEqual(true);
}
catch (e) {
fail();
}
}
});
it('Detects invalid file names', function() {
var fileNames = [
'',
' ',
'.',
'..',
'back\\slash',
'sl/ash',
'lt<lt',
'gt>gt',
'col:on',
'double"quote',
'pi|pe',
'dont?ask?questions?',
'super*star',
'new\nline',
' ..',
'.. ',
'. ',
' .'
];
for ( var i = 0; i < fileNames.length; i++ ) {
var threwException = false;
try {
Files.isFileNameValid(fileNames[i]);
fail();
}
catch (e) {
threwException = true;
}
expect(threwException).toEqual(true);
}
});
});
});

37
autotest-js.sh Executable file
View File

@ -0,0 +1,37 @@
#!/bin/bash
#
# ownCloud
#
# Run JS tests
#
# @author Vincent Petry
# @copyright 2014 Vincent Petry <pvince81@owncloud.com>
#
NPM="$(which npm 2>/dev/null)"
PREFIX="build"
if test -z "$NPM"
then
echo 'Node JS >= 0.8 is required to run the JavaScript tests' >&2
exit 1
fi
# update/install test packages
mkdir -p "$PREFIX" && $NPM install --link --prefix "$PREFIX" || exit 3
KARMA="$(which karma 2>/dev/null)"
# If not installed globally, try local version
if test -z "$KARMA"
then
KARMA="$PREFIX/node_modules/karma/bin/karma"
fi
if test -z "$KARMA"
then
echo 'Karma module executable not found' >&2
exit 2
fi
KARMA_TESTSUITE="$1" $KARMA start tests/karma.config.js --single-run

19
build/package.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "owncloud-js-tests",
"description": "ownCloud tests",
"version": "0.0.1",
"author": {
"name": "Vincent Petry",
"email": "pvince81@owncloud.com"
},
"private": true,
"homepage": "https://github.com/owncloud/",
"contributors": [],
"dependencies": {},
"devDependencies": {
"karma": "*",
"karma-jasmine": "*",
"karma-junit-reporter": "*"
},
"engine": "node >= 0.8"
}

28
core/js/core.json Normal file
View File

@ -0,0 +1,28 @@
{
"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"
]
}

View File

@ -3,9 +3,12 @@ OC.Router = {
// register your ajax requests to load after the loading of the routes
// has finished. otherwise you face problems with race conditions
registerLoadedCallback: function(callback){
if (!this.routes_request){
return;
}
this.routes_request.done(callback);
},
routes_request: $.ajax(OC.router_base_url + '/core/routes.json', {
routes_request: !window.TESTING && $.ajax(OC.router_base_url + '/core/routes.json', {
dataType: 'json',
success: function(jsondata) {
if (jsondata.status === 'success') {
@ -75,4 +78,4 @@ OC.Router = {
return OC.router_base_url + url;
}
};
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,89 @@
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/**
* Simulate the variables that are normally set by PHP code
*/
// from core/js/config.php
window.TESTING = true;
window.oc_debug = true;
window.datepickerFormatDate = 'MM d, yy';
window.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday'
];
window.monthNames = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
window.firstDay = 0;
// setup dummy webroots
window.oc_webroot = location.href + '/';
window.oc_appswebroots = {
"files": window.oc_webroot + '/apps/files/'
};
// global setup for all tests
(function setupTests() {
var fakeServer = null;
beforeEach(function() {
// enforce fake XHR, tests should not depend on the server and
// must use fake responses for expected calls
fakeServer = sinon.fakeServer.create();
// return fake translations as they might be requested for many test runs
fakeServer.respondWith(/\/index.php\/core\/ajax\/translations.php$/, [
200, {
"Content-Type": "application/json"
},
'{"data": [], "plural_form": "nplurals=2; plural=(n != 1);"}'
]);
// make it globally available, so that other tests can define
// custom responses
window.fakeServer = fakeServer;
});
afterEach(function() {
// uncomment this to log requests
// console.log(window.fakeServer.requests);
fakeServer.restore();
});
})();

View File

@ -0,0 +1,70 @@
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
describe('Core base tests', function() {
describe('Base values', function() {
it('Sets webroots', function() {
expect(OC.webroot).toBeDefined();
expect(OC.appswebroots).toBeDefined();
});
});
describe('Link functions', function() {
var TESTAPP = 'testapp';
var TESTAPP_ROOT = OC.webroot + '/appsx/testapp';
beforeEach(function() {
OC.appswebroots[TESTAPP] = TESTAPP_ROOT;
});
afterEach(function() {
// restore original array
delete OC.appswebroots[TESTAPP];
});
it('Generates correct links for core apps', function() {
expect(OC.linkTo('core', 'somefile.php')).toEqual(OC.webroot + '/core/somefile.php');
expect(OC.linkTo('admin', 'somefile.php')).toEqual(OC.webroot + '/admin/somefile.php');
});
it('Generates correct links for regular apps', function() {
expect(OC.linkTo(TESTAPP, 'somefile.php')).toEqual(OC.webroot + '/index.php/apps/' + TESTAPP + '/somefile.php');
});
it('Generates correct remote links', function() {
expect(OC.linkToRemote('webdav')).toEqual(window.location.protocol + '//' + window.location.host + OC.webroot + '/remote.php/webdav');
});
describe('Images', function() {
it('Generates image path with given extension', function() {
var svgSupportStub = sinon.stub(window, 'SVGSupport', function() { return true; });
expect(OC.imagePath('core', 'somefile.jpg')).toEqual(OC.webroot + '/core/img/somefile.jpg');
expect(OC.imagePath(TESTAPP, 'somefile.jpg')).toEqual(TESTAPP_ROOT + '/img/somefile.jpg');
svgSupportStub.restore();
});
it('Generates image path with svg extension when svg support exists', function() {
var svgSupportStub = sinon.stub(window, 'SVGSupport', function() { return true; });
expect(OC.imagePath('core', 'somefile')).toEqual(OC.webroot + '/core/img/somefile.svg');
expect(OC.imagePath(TESTAPP, 'somefile')).toEqual(TESTAPP_ROOT + '/img/somefile.svg');
svgSupportStub.restore();
});
it('Generates image path with png ext when svg support is not available', function() {
var svgSupportStub = sinon.stub(window, 'SVGSupport', function() { return false; });
expect(OC.imagePath('core', 'somefile')).toEqual(OC.webroot + '/core/img/somefile.png');
expect(OC.imagePath(TESTAPP, 'somefile')).toEqual(TESTAPP_ROOT + '/img/somefile.png');
svgSupportStub.restore();
});
});
});
});

View File

@ -293,6 +293,7 @@ class OC {
public static function initTemplateEngine() {
// Add the stuff we need always
// TODO: read from core/js/core.json
OC_Util::addScript("jquery-1.10.0.min");
OC_Util::addScript("jquery-migrate-1.2.1.min");
OC_Util::addScript("jquery-ui-1.10.0.custom");

138
tests/karma.config.js Normal file
View File

@ -0,0 +1,138 @@
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/**
* This node module is run by the karma executable to specify its configuration.
*
* The list of files from all needed JavaScript files including the ones from the
* apps to test, and the test specs will be passed as configuration object.
*
* Note that it is possible to test a single app by setting the KARMA_TESTSUITE
* environment variable to the apps name, for example "core" or "files_encryption".
* Multiple apps can be specified by separating them with space.
*
*/
module.exports = function(config) {
// default apps to test when none is specified (TODO: read from filesystem ?)
var defaultApps = 'core files';
var appsToTest = process.env.KARMA_TESTSUITE || defaultApps;
// 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 testCore = false;
var files = [];
var index;
// find out what apps to test from appsToTest
appsToTest = appsToTest.split(' ');
index = appsToTest.indexOf('core');
if (index > -1) {
appsToTest.splice(index, 1);
testCore = true;
}
// extra test libs
files.push(corePath + 'tests/lib/sinon-1.7.3.js');
// core mocks
files.push(corePath + 'tests/specHelper.js');
// add core files
for ( var i = 0; i < coreFiles.length; i++ ) {
files.push( corePath + coreFiles[i] );
}
// need to test the core app as well ?
if (testCore) {
// core tests
files.push(corePath + 'tests/specs/*.js');
}
for ( var i = 0; i < appsToTest.length; i++ ) {
// add app JS
files.push('apps/' + appsToTest[i] + '/js/*.js');
// add test specs
files.push('apps/' + appsToTest[i] + '/tests/js/*.js');
}
config.set({
// base path, that will be used to resolve files and exclude
basePath: '..',
// frameworks to use
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: files,
// list of files to exclude
exclude: [
],
// test results reporter to use
// possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
reporters: ['dots', 'junit'],
junitReporter: {
outputFile: 'tests/autotest-results-js.xml'
},
// 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: false,
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera (has to be installed with `npm install karma-opera-launcher`)
// - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
// - PhantomJS
// - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
browsers: ['PhantomJS'],
// If browser does not capture in given timeout [ms], kill it
captureTimeout: 60000,
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: false
});
};