Intern-based unit tests

This commit adds unit-tests runner for UI and 1 test suite for
the expression parser. It also makes --webui flag for run_tests.sh
run all the UI tests: lint checks, unit tests, functional tests.

Change-Id: I6176dc01cdd6d2bcf7a99185e92cc498c7b2b899
This commit is contained in:
Vitaly Kramskikh 2015-03-22 16:04:40 +03:00
parent 0d9f7e395e
commit 54edb12673
13 changed files with 1636 additions and 594 deletions

1
.gitignore vendored
View File

@ -24,6 +24,7 @@ lock
node_modules
bower_components
nailgun/static/vendor/bower
nailgun/static/tests/bower
*.egg
.testrepository

View File

@ -95,7 +95,7 @@ Setup for Nailgun Unit Tests
#. Run the Nailgun backend unit tests::
sudo apt-get install puppet-common #install missing package required by tasklib tests
./run_tests.sh --no-lint-ui --no-webui
./run_tests.sh --no-webui
#. Run the Nailgun flake8 test::
@ -145,6 +145,9 @@ For example:
Setup for Web UI Tests
----------------------
#. UI tests use Selenium server, so you need to install Java Runtime
Environment (JRE) 1.6 or newer version.
#. Install NodeJS and JS dependencies::
sudo apt-get remove nodejs nodejs-legacy
@ -161,7 +164,6 @@ Setup for Web UI Tests
PostgreSQL)::
cd fuel-web
./run_tests.sh --lint-ui
./run_tests.sh --webui

View File

@ -49,9 +49,8 @@ implement this feature, these steps were followed:
#. Test your Nailgun changes::
cd fuel-web
./run_tests.sh --no-lint-ui --no-webui
./run_tests.sh --no-webui
./run_tests.sh --flake8
./run_tests.sh --lint-ui
./run_tests.sh --webui
#. You should also test Nailgun in fake UI mode by following

View File

@ -22,6 +22,9 @@
"bootstrap": "~3.3.4",
"open-sans-fontface": "~1.4.0"
},
"devDependencies": {
"es5-shim": "4.1.0"
},
"overrides": {
"jquery": {
"main": "jquery.js"

View File

@ -20,12 +20,15 @@ var argv = require('minimist')(process.argv.slice(2));
var fs = require('fs');
var path = require('path');
var glob = require('glob');
var spawn = require('child_process').spawn;
var rimraf = require('rimraf');
var es = require('event-stream');
var _ = require('lodash-node');
var gulp = require('gulp');
var gutil = require('gulp-util');
var shell = require('gulp-shell');
var runSequence = require('run-sequence');
var bower = require('gulp-bower');
@ -101,23 +104,106 @@ gulp.task('i18n:validate', function() {
gulp.task('bower:fetch', bower);
gulp.task('bower:copy-main', function() {
var bowerDir = 'static/vendor/bower/';
rimraf.sync(bowerDir);
return gulp.src(mainBowerFiles({checkExistence: true}), {base: 'bower_components'})
.pipe(gulp.dest(bowerDir));
var dirs = [
{dirName: 'static/vendor/bower', includeDev: 'inclusive'},
{dirName: 'static/tests/bower', includeDev: 'exclusive'}
];
var streams = dirs.map(function(dir) {
rimraf.sync(dir.dirName);
return gulp.src(mainBowerFiles({checkExistence: true, includeDev: dir.includeDev}), {base: 'bower_components'})
.pipe(gulp.dest(dir.dirName));
});
return es.merge(streams);
});
gulp.task('bower', function(cb) {
runSequence('bower:fetch', 'bower:copy-main', cb);
});
var selenium = require('selenium-standalone');
var seleniumProcess = null;
function shutdownSelenium() {
if (seleniumProcess) {
seleniumProcess.kill();
seleniumProcess = null;
}
}
gulp.task('selenium:fetch', function(cb) {
var defaultVersion = '2.45.0';
selenium.install({version: argv.version || defaultVersion}, cb);
});
gulp.task('selenium', ['selenium:fetch'], function(cb) {
var port = process.env.SELENIUM_SERVER_PORT || 4444;
selenium.start(
{seleniumArgs: ['--port', port], spawnOptions: {stdio: 'pipe'}},
function(err, child) {
if (err) throw err;
child.on('exit', function() {
if (seleniumProcess) {
gutil.log(gutil.colors.yellow('Selenium process died unexpectedly. Probably port', port, 'is already in use.'));
}
});
['exit', 'uncaughtException', 'SIGTERM', 'SIGINT'].forEach(function(event) {
process.on(event, shutdownSelenium);
});
seleniumProcess = child;
cb();
}
);
});
function runIntern(params) {
return function() {
var baseDir = 'static';
var runner = './node_modules/.bin/intern-runner';
var browser = params.browser || argv.browser || 'phantomjs';
var options = [['config', 'tests/intern-' + browser + '.js']];
var suiteOptions = [];
['suites', 'functionalSuites'].forEach(function(suiteType) {
if (params[suiteType]) {
var suiteFiles = glob.sync(path.relative(baseDir, params[suiteType]), {cwd: baseDir});
suiteOptions = suiteOptions.concat(suiteFiles.map(function(suiteFile) {
return [suiteType, suiteFile.replace(/\.js$/, '')];
}));
}
});
if (!suiteOptions.length) {
throw new Error('No matching suites');
}
options = options.concat(suiteOptions);
var command = [path.relative(baseDir, runner)].concat(options.map(function(o) {
return o.join('=');
})).join(' ');
gutil.log('Executing', command);
return shell.task(command, {cwd: baseDir})();
};
}
gulp.task('intern:unit', runIntern({suites: argv.suites || 'static/tests/unit/**/*.js'}));
gulp.task('unit-tests', function(cb) {
runSequence('selenium', 'intern:unit', function(err) {
shutdownSelenium();
cb(err);
});
});
gulp.task('jison', function() {
return gulp.src('static/expression/parser.jison')
.pipe(jison({moduleType: 'js'}))
.pipe(gulp.dest('static/expression/'));
});
var jsFiles = ['static/**/*.js', 'static/**/*.jsx', '!static/vendor/**', '!static/expression/parser.js'];
var jsFiles = [
'static/**/*.js',
'static/**/*.jsx',
'!static/vendor/**',
'!static/expression/parser.js',
'static/tests/**/*.js',
'!static/tests/bower/**/*.js'
];
var styleFiles = 'static/**/*.less';
gulp.task('jscs', function() {

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,8 @@
"bower": "~1.3.12",
"casperjs": "~1.1.0-beta3",
"esprima-fb": "13001.1001.0-dev-harmony-fb",
"event-stream": "~3.3.1",
"glob": "~5.0.5",
"gulp": "~3.8.11",
"gulp-autoprefixer": "~2.1.0",
"gulp-bower": "~0.0.10",
@ -21,14 +23,18 @@
"gulp-lintspaces": "~0.2.3",
"gulp-react": "~3.0.1",
"gulp-replace": "~0.5.3",
"gulp-shell": "~0.4.1",
"gulp-util": "~3.0.4",
"intern": "~2.2.2",
"jshint-stylish": "~1.0.1",
"lodash-node": "~2.4.1",
"main-bower-files": "~2.6.2",
"minimist": "~1.1.1",
"phantomjs": "~1.9.16",
"requirejs": "~2.1.16",
"rimraf": "~2.2.8",
"run-sequence": "~1.0.2",
"selenium-standalone": "~4.4.0",
"uglify-js": "~2.4.16"
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2015 Mirantis, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
**/
define(['./intern'], function(config) {
'use strict';
config.environments = [{browserName: 'chrome'}];
return config;
});

View File

@ -0,0 +1,21 @@
/*
* Copyright 2015 Mirantis, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
**/
define(['./intern'], function(config) {
'use strict';
config.environments = [{browserName: 'firefox'}];
return config;
});

View File

@ -0,0 +1,27 @@
/*
* Copyright 2015 Mirantis, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
**/
define(['./intern'], function(config) {
'use strict';
config.environments = [{
browserName: 'phantomjs',
'phantomjs.binary.path': 'node_modules/phantomjs/lib/phantom/bin/phantomjs'
}];
config.loader.shim.react = {
deps: ['tests/bower/es5-shim/es5-shim']
};
return config;
});

View File

@ -0,0 +1,38 @@
/*
* Copyright 2015 Mirantis, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
**/
define(['config'], function(config) {
'use strict';
config.baseUrl = '';
config.waitSeconds = 7;
return {
proxyPort: 9057,
proxyUrl: 'http://localhost:9057/',
capabilities: {
'selenium-version': '2.45.0'
},
maxConcurrency: 1,
useLoader: {
'host-node': 'requirejs',
'host-browser': '/vendor/bower/requirejs/require.js'
},
// A regular expression matching URLs to files that should not be included in code coverage analysis
excludeInstrumentation: /^/,
loader: config
};
});

View File

@ -0,0 +1,82 @@
define([
'intern!object',
'intern/chai!assert',
'underscore',
'models',
'expression',
'expression/objects'
], function(registerSuite, assert, _, models, Expression, expressionObjects) {
'use strict';
registerSuite({
'Expression parser test': function() {
var hypervisor = 'kvm';
var testModels = {
cluster: new models.Cluster({mode: 'ha_compact'}),
settings: new models.Settings({common: {libvirt_type: {value: hypervisor}}}),
release: new models.Release({roles: ['controller', 'compute']})
};
var testCases = [
// test scalars
['true', true],
['false', false],
['123', 123],
['"123"', '123'],
["'123'", '123'],
// test null
['null', null],
['null == false', false],
['null == true', false],
['null == null', true],
// test boolean operators
['true or false', true],
['true and false', false],
['not true', false],
// test precedence
['true or true and false or false', true],
['true == true and false == false', true],
// test comparison
['123 == 123', true],
['123 == 321', false],
['123 != 321', true],
['123 != "123"', true],
// test grouping
['(true or true) and not (false or false)', true],
// test errors
['(true', Error],
['false and', Error],
['== 123', Error],
['#^@$*()#@!', Error],
// test modelpaths
['cluster:mode', 'ha_compact'],
['cluster:mode == "ha_compact"', true],
['cluster:mode != "multinode"', true],
['"controller" in release:roles', true],
['"unknown-role" in release:roles', false],
['settings:common.libvirt_type.value', hypervisor],
['settings:common.libvirt_type.value == "' + hypervisor + '"', true],
['cluster:mode == "ha_compact" and not (settings:common.libvirt_type.value != "' + hypervisor + '")', true],
// test nonexistent keys
['cluster:nonexistentkey', Error],
// test evaluation flow
['cluster:mode != "ha_compact" and cluster:nonexistentkey == 1', false],
['cluster:mode == "ha_compact" and cluster:nonexistentkey == 1', Error]
];
function evaluate(expression) {
var result = Expression(expression, testModels).evaluate();
return result instanceof expressionObjects.ModelPath ? result.get() : result;
}
_.each(testCases, function(testCase) {
var expression = testCase[0];
var result = testCase[1];
if (result === Error) {
assert.throws(evaluate.bind(null, expression), Error, '', expression + ' throws an error');
} else {
assert.strictEqual(evaluate(expression), result, expression + ' evaluates correctly');
}
});
}
});
});

View File

@ -25,8 +25,6 @@ function usage {
echo " -h, --help Print this usage message"
echo " -k, --tasklib Run tasklib unit and functional tests"
echo " -K, --no-tasklib Don't run tasklib unit and functional tests"
echo " -l, --lint-ui Run UI linting tasks"
echo " -L, --no-lint-ui Don't run UI linting tasks"
echo " -n, --nailgun Run NAILGUN unit/integration tests"
echo " -N, --no-nailgun Don't run NAILGUN unit/integration tests"
echo " -x, --performance Run NAILGUN performance tests"
@ -37,8 +35,14 @@ function usage {
echo " -t, --tests Run a given test files"
echo " -u, --upgrade Run tests for UPGRADE system"
echo " -U, --no-upgrade Don't run tests for UPGRADE system"
echo " -w, --webui Run WEB-UI tests"
echo " -W, --no-webui Don't run WEB-UI tests"
echo " -w, --webui Run all UI tests"
echo " -W, --no-webui Don't run all UI tests"
echo " --ui-lint Run UI linting tasks"
echo " --no-ui-lint Don't run UI linting tasks"
echo " --ui-unit Run UI unit tests"
echo " --no-ui-unit Don't run UI unit tests"
echo " --ui-func Run UI functional tests"
echo " --no-ui-func Don't run UI functional tests"
echo ""
echo "Note: with no options specified, the script will try to run all available"
echo " tests with all available checks."
@ -57,16 +61,20 @@ function process_options {
-x|--performance) performance_tests=1;;
-k|--tasklib) tasklib_tests=1;;
-K|--no-tasklib) no_tasklib_tests=1;;
-w|--webui) webui_tests=1;;
-W|--no-webui) no_webui_tests=1;;
-u|--upgrade) upgrade_system=1;;
-U|--no-upgrade) no_upgrade_system=1;;
-s|--shotgun) shotgun_tests=1;;
-S|--no-shotgun) no_shotgun_tests=1;;
-p|--flake8) flake8_checks=1;;
-P|--no-flake8) no_flake8_checks=1;;
-l|--lint-ui) lint_ui_checks=1;;
-L|--no-lint-ui) no_lint_ui_checks=1;;
-w|--webui) ui_lint_checks=1; ui_unit_tests=1; ui_func_tests=1;;
-W|--no-webui) no_ui_lint_checks=1; no_ui_unit_tests=1; no_ui_func_tests=1;;
--ui-lint) ui_lint_checks=1;;
--no-ui-lint) no_ui_lint_checks=1;;
--ui-unit) ui_unit_tests=1;;
--no-ui-unit) no_ui_unit_tests=1;;
--ui-func) ui_func_tests=1;;
--no-ui-func) no_ui_func_tests=1;;
-t|--tests) certain_tests=1;;
-*) testropts="$testropts $arg";;
*) testrargs="$testrargs $arg"
@ -109,16 +117,18 @@ no_agent_tests=0
nailgun_tests=0
no_nailgun_tests=0
performance_tests=0
webui_tests=0
no_webui_tests=0
upgrade_system=0
no_upgrade_system=0
shotgun_tests=0
no_shotgun_tests=0
flake8_checks=0
no_flake8_checks=0
lint_ui_checks=0
no_lint_ui_checks=0
ui_lint_checks=0
no_ui_lint_checks=0
ui_unit_tests=0
no_ui_unit_tests=0
ui_func_tests=0
no_ui_func_tests=0
certain_tests=0
tasklib_tests=0
no_tasklib_tests=0
@ -150,20 +160,22 @@ function run_tests {
$nailgun_tests -eq 0 && \
$performance_tests -eq 0 && \
$tasklib_tests -eq 0 && \
$webui_tests -eq 0 && \
$ui_lint_checks -eq 0 && \
$ui_unit_tests -eq 0 && \
$ui_func_tests -eq 0 && \
$upgrade_system -eq 0 && \
$shotgun_tests -eq 0 && \
$flake8_checks -eq 0 && \
$lint_ui_checks -eq 0 ]]; then
$flake8_checks -eq 0 ]]; then
if [ $no_agent_tests -ne 1 ]; then agent_tests=1; fi
if [ $no_nailgun_tests -ne 1 ]; then nailgun_tests=1; fi
if [ $no_tasklib_tests -ne 1 ]; then tasklib_tests=1; fi
if [ $no_webui_tests -ne 1 ]; then webui_tests=1; fi
if [ $no_ui_lint_checks -ne 1 ]; then ui_lint_checks=1; fi
if [ $no_ui_unit_tests -ne 1 ]; then ui_unit_tests=1; fi
if [ $no_ui_func_tests -ne 1 ]; then ui_func_tests=1; fi
if [ $no_upgrade_system -ne 1 ]; then upgrade_system=1; fi
if [ $no_shotgun_tests -ne 1 ]; then shotgun_tests=1; fi
if [ $no_flake8_checks -ne 1 ]; then flake8_checks=1; fi
if [ $no_lint_ui_checks -ne 1 ]; then lint_ui_checks=1; fi
fi
# Run all enabled tests
@ -187,9 +199,19 @@ function run_tests {
run_tasklib_tests || errors+=" tasklib tests"
fi
if [ $webui_tests -eq 1 ]; then
echo "Starting WebUI tests..."
run_webui_tests || errors+=" webui_tests"
if [ $ui_lint_checks -eq 1 ]; then
echo "Starting UI lint checks..."
run_lint_ui || errors+=" ui_lint_checks"
fi
if [ $ui_unit_tests -eq 1 ]; then
echo "Starting UI unit tests..."
run_ui_unit_tests || errors+=" ui_unit_tests"
fi
if [ $ui_func_tests -eq 1 ]; then
echo "Starting UI functional tests..."
run_ui_func_tests || errors+=" ui_func_tests"
fi
if [ $upgrade_system -eq 1 ]; then
@ -202,11 +224,6 @@ function run_tests {
run_shotgun_tests || errors+=" shotgun_tests"
fi
if [ $lint_ui_checks -eq 1 ]; then
echo "Starting lint checks..."
run_lint_ui || errors+=" lint_ui_checks"
fi
# print failed tests
if [ -n "$errors" ]; then
echo Failed tests: $errors
@ -272,16 +289,30 @@ function run_nailgun_tests {
return $result
}
# Run webui tests.
# Run UI unit tests.
#
function run_ui_unit_tests {
local result=0
pushd $ROOT/nailgun >> /dev/null
${GULP} bower && ${GULP} unit-tests || result=1
popd >> /dev/null
return $result
}
# Run UI functional tests.
#
# Arguments:
#
# $@ -- tests to be run; with no arguments all tests will be run
function run_webui_tests {
function run_ui_func_tests {
local SERVER_PORT=$UI_SERVER_PORT
local TESTS_DIR=$ROOT/nailgun/ui_tests
local TESTS=$TESTS_DIR/test_*.js
local artifacts=$ARTIFACTS/webui
local artifacts=$ARTIFACTS/ui_func
local config=$artifacts/test.yaml
prepare_artifacts $artifacts $config
local COMPRESSED_STATIC_DIR=$artifacts/static_compressed
@ -594,7 +625,7 @@ EOL
# $1 -- path to the test file
function guess_test_run {
if [[ $1 == *ui_tests* && $1 == *.js ]]; then
run_webui_tests $1 || echo "ERROR: $1"
run_ui_func_tests $1 || echo "ERROR: $1"
elif [[ $1 == *fuel_upgrade_system* ]]; then
run_upgrade_system_tests $1 || echo "ERROR: $1"
elif [[ $1 == *shotgun* ]]; then