diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..8a6ee2e --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,162 @@ +/* + * + * 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. + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); + +module.exports = function (config) { + // This tox venv is setup in the post-install npm step + var xstaticPath = path.resolve('./.tox/npm'); + + if (!xstaticPath) { + console.error('xStatic libraries not found, please run `tox -e npm`'); + process.exit(1); + } + xstaticPath += '/lib/'; + xstaticPath += fs.readdirSync(xstaticPath).find(function(directory) { + return directory.indexOf('python') === 0; + }); + + var toxPath = xstaticPath + '/site-packages/'; + xstaticPath = toxPath + 'xstatic/pkg/'; + + config.set({ + preprocessors: { + // Used to collect templates for preprocessing. + // NOTE: the templates must also be listed in the files section below. + './static/**/*.html': ['ng-html2js'] + }, + + // Sets up module to process templates. + ngHtml2JsPreprocessor: { + prependPrefix: '/', + moduleName: 'templates' + }, + + basePath: './', + + // Contains both source and test files. + files: [ + /* + * shim, partly stolen from /i18n/js/horizon/ + * Contains expected items not provided elsewhere (dynamically by + * Django or via jasmine template. + */ + './test-shim.js', + + // from jasmine.html + xstaticPath + 'jquery/data/jquery.js', + xstaticPath + 'angular/data/angular.js', + xstaticPath + 'angular/data/angular-route.js', + xstaticPath + 'angular/data/angular-mocks.js', + xstaticPath + 'angular/data/angular-cookies.js', + xstaticPath + 'angular_bootstrap/data/angular-bootstrap.js', + xstaticPath + 'angular_gettext/data/angular-gettext.js', + xstaticPath + 'angular_fileupload/data/ng-file-upload-all.js', + xstaticPath + 'angular/data/angular-sanitize.js', + xstaticPath + 'd3/data/d3.js', + xstaticPath + 'rickshaw/data/rickshaw.js', + xstaticPath + 'angular_smart_table/data/smart-table.js', + xstaticPath + 'angular_lrdragndrop/data/lrdragndrop.js', + xstaticPath + 'spin/data/spin.js', + xstaticPath + 'spin/data/spin.jquery.js', + xstaticPath + 'tv4/data/tv4.js', + xstaticPath + 'objectpath/data/ObjectPath.js', + xstaticPath + 'angular_schema_form/data/schema-form.js', + + // TODO: These should be mocked. + toxPath + '/horizon/static/horizon/js/horizon.js', + + /** + * Include framework source code from horizon that we need. + * Otherwise, karma will not be able to find them when testing. + * These files should be mocked in the foreseeable future. + */ + toxPath + 'horizon/static/framework/**/*.module.js', + toxPath + 'horizon/static/framework/**/!(*.spec|*.mock).js', + toxPath + 'openstack_dashboard/static/**/*.module.js', + toxPath + 'openstack_dashboard/static/**/!(*.spec|*.mock).js', + toxPath + 'openstack_dashboard/dashboards/**/static/**/*.module.js', + toxPath + 'openstack_dashboard/dashboards/**/static/**/!(*.spec|*.mock).js', + + './vitrage_dashboard/dashboard/static/vendor/lodash/**/lodash.js', + './vitrage_dashboard/dashboard/static/vendor/graphlib/**/graphlib.core.js', + './vitrage_dashboard/dashboard/static/vendor/dagre/**/dagre.core.js', + './vitrage_dashboard/dashboard/static/vendor/d3/**/d3.js', + './vitrage_dashboard/dashboard/static/vendor/dagre-d3/**/dagre-d3.core.js', + //'./vitrage_dashboard/dashboard/static/**/*.module.js', + //'./vitrage_dashboard/dashboard/static/**/*.module.js', + + /** + * First, list all the files that defines application's angular modules. + * Those files have extension of `.module.js`. The order among them is + * not significant. + */ + './vitrage_dashboard/dashboard/static/**/*.module.js', + + /** + * Followed by other JavaScript files that defines angular providers + * on the modules defined in files listed above. And they are not mock + * files or spec files defined below. The order among them is not + * significant. + */ + './vitrage_dashboard/dashboard/static/**/!(*.spec|*.mock).js', + + /** + * Then, list files for mocks with `mock.js` extension. The order + * among them should not be significant. + */ + toxPath + 'openstack_dashboard/static/**/*.mock.js', + //'./static/**/*.mock.js', + + /** + * Finally, list files for spec with `spec.js` extension. The order + * among them should not be significant. + */ + './vitrage_dashboard/dashboard/static/app/core/openstack-service-api/vitrage.service.spec.js', + + /** + * Angular external templates + */ + './vitrage_dashboard/dashboard/static/**/*.html', + + ], + + autoWatch: true, + + frameworks: ['jasmine'], + + browsers: ['Chrome'], + + phantomjsLauncher: { + // Have phantomjs exit if a ResourceError is encountered + // (useful if karma exits without killing phantom) + exitOnResourceError: true + }, + + reporters: ['progress'], + //reporters: ['spec'], + + plugins: [ + 'karma-chrome-launcher', + 'karma-jasmine', + 'karma-ng-html2js-preprocessor', + 'karma-spec-reporter', + ] + + }); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..0193b8a --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "version": "0.0.0", + "private": true, + "name": "vitrage-dashboard", + "description": "Vitrage Dashboard", + "repository": "none", + "license": "Apache 2.0", + "devDependencies": { + "eslint": "1.10.3", + "eslint-config-openstack": "1.2.4", + "jasmine-core": "2.4.1", + "karma": "1.1.2", + "karma-chrome-launcher": "1.0.1", + "karma-cli": "^1.0.1", + "karma-jasmine": "1.0.2", + "karma-ng-html2js-preprocessor": "1.0.0", + "karma-spec-reporter": "0.0.32" + }, + "scripts": { + "postinstall": "if [ ! -d .venv ]; then tox -e npm --notest; fi", + "lint": "eslint --no-color vitrage_dashboard/static", + "lintq": "eslint --quiet vitrage_dashboard/static", + "test": "karma start karma.conf.js --single-run" + }, + "dependencies": {} +} diff --git a/requirements.txt b/requirements.txt index 3ba6084..8052758 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 # Horizon Core Requirements -Django<1.11,>=1.8 # BSD django-compressor>=2.0 # MIT iso8601>=0.1.11 # MIT + +horizon>=14.0.0.0b1 # Apache-2.0 diff --git a/test-shim.js b/test-shim.js new file mode 100644 index 0000000..176b1c3 --- /dev/null +++ b/test-shim.js @@ -0,0 +1,110 @@ +/** + * 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. + */ + +/* + * Shim for Javascript unit tests; supplying expected global features. + * This should be removed from the codebase once i18n services are provided. + * Taken from default i18n file provided by Django. + */ + +var horizonPlugInModules = []; + + +(function (globals) { + + var django = globals.django || (globals.django = {}); + + + django.pluralidx = function (count) { return (count == 1) ? 0 : 1; }; + + /* gettext identity library */ + + django.gettext = function (msgid) { return msgid; }; + django.ngettext = function (singular, plural, count) { return (count == 1) ? singular : plural; }; + django.gettext_noop = function (msgid) { return msgid; }; + django.pgettext = function (context, msgid) { return msgid; }; + django.npgettext = function (context, singular, plural, count) { return (count == 1) ? singular : plural; }; + + + django.interpolate = function (fmt, obj, named) { + if (named) { + return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])}); + } else { + return fmt.replace(/%s/g, function(match){return String(obj.shift())}); + } + }; + + + /* formatting library */ + + django.formats = { + "DATETIME_FORMAT": "N j, Y, P", + "DATETIME_INPUT_FORMATS": [ + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d %H:%M:%S.%f", + "%Y-%m-%d %H:%M", + "%Y-%m-%d", + "%m/%d/%Y %H:%M:%S", + "%m/%d/%Y %H:%M:%S.%f", + "%m/%d/%Y %H:%M", + "%m/%d/%Y", + "%m/%d/%y %H:%M:%S", + "%m/%d/%y %H:%M:%S.%f", + "%m/%d/%y %H:%M", + "%m/%d/%y" + ], + "DATE_FORMAT": "N j, Y", + "DATE_INPUT_FORMATS": [ + "%Y-%m-%d", + "%m/%d/%Y", + "%m/%d/%y" + ], + "DECIMAL_SEPARATOR": ".", + "FIRST_DAY_OF_WEEK": "0", + "MONTH_DAY_FORMAT": "F j", + "NUMBER_GROUPING": "3", + "SHORT_DATETIME_FORMAT": "m/d/Y P", + "SHORT_DATE_FORMAT": "m/d/Y", + "THOUSAND_SEPARATOR": ",", + "TIME_FORMAT": "P", + "TIME_INPUT_FORMATS": [ + "%H:%M:%S", + "%H:%M:%S.%f", + "%H:%M" + ], + "YEAR_MONTH_FORMAT": "F Y" + }; + + django.get_format = function (format_type) { + var value = django.formats[format_type]; + if (typeof(value) == 'undefined') { + return format_type; + } else { + return value; + } + }; + + /* add to global namespace */ + globals.pluralidx = django.pluralidx; + globals.gettext = django.gettext; + globals.ngettext = django.ngettext; + globals.gettext_noop = django.gettext_noop; + globals.pgettext = django.pgettext; + globals.npgettext = django.npgettext; + globals.interpolate = django.interpolate; + globals.get_format = django.get_format; + globals.STATIC_URL = '/static/'; + globals.WEBROOT = '/'; + +}(this)); diff --git a/tox.ini b/tox.ini index 2785ca8..209d8a9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 1.6 -envlist = py35,py27,pep8 +envlist = py35,py27,pep8,npm skipsdist = True [testenv] @@ -17,6 +17,18 @@ basepython = python3 commands = flake8 python setup.py check --restructuredtext --strict +[testenv:npm] +basepython = python3 +passenv = + HOME + DISPLAY +commands = + nodeenv -p + npm install + npm run {posargs:test} +deps = + -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt [testenv:venv] basepython = python3 commands = {posargs} diff --git a/vitrage_dashboard/dashboard/static/app/core/openstack-service-api/vitrage.service.js b/vitrage_dashboard/dashboard/static/app/core/openstack-service-api/vitrage.service.js index 654b5c0..ded55f9 100644 --- a/vitrage_dashboard/dashboard/static/app/core/openstack-service-api/vitrage.service.js +++ b/vitrage_dashboard/dashboard/static/app/core/openstack-service-api/vitrage.service.js @@ -38,7 +38,7 @@ } console.info('CONFIG in core - ', config) return apiService.get('/api/vitrage/topology/', config) - .catch(function () { + .error(function () { toastService.add('error', gettext('Unable to fetch the Vitrage Topology service.')); }); } diff --git a/vitrage_dashboard/dashboard/static/app/core/openstack-service-api/vitrage.service.spec.js b/vitrage_dashboard/dashboard/static/app/core/openstack-service-api/vitrage.service.spec.js index c8832d7..e2e25b5 100644 --- a/vitrage_dashboard/dashboard/static/app/core/openstack-service-api/vitrage.service.spec.js +++ b/vitrage_dashboard/dashboard/static/app/core/openstack-service-api/vitrage.service.spec.js @@ -44,7 +44,8 @@ "func": "getTopology", "method": "get", "path": "/api/vitrage/topology/", - "error": "Unable to get the Vitrage service topology." + "data": {}, + "error": "Unable to fetch the Vitrage Topology service." } ];