Initial commit

This contains the initial commit for the storyboard web client
project, consisting of the basic build & testing harnesses,
simple set of routes, and a list of basic dependencies
necessary to run an application. It's purpose is to be
reference Javascript/Angular project to test out the build
images.
This commit is contained in:
Michael Krotscheck 2013-12-19 07:27:10 -08:00
commit 197d5dd2bd
56 changed files with 2720 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
.build
.local
node_modules
bower_components
dist
npm-debug.log
*.iml
.idea
reports
cover

4
.gitreview Normal file
View File

@ -0,0 +1,4 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack-infra/storyboard-webclient.git

65
.jshintrc Normal file
View File

@ -0,0 +1,65 @@
// Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
//
// 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.
/**
* This file contains JSHint configuration settings, which allow us to enforce a
* coding standard programatically using jshint. It's as close to PEP-8 as we
* can get it.
*
* @see http://www.jshint.com/
*/
{
"node": true,
"browser": true,
"esnext": true,
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 4,
"maxlen": 80,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": "single",
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"smarttabs": true,
// Inform JSHint that the following globals are expected.
"globals": {
// Library constants
"$": false, // JQuery
"angular": false, // AngularJS
// Unit test constants
"after": false,
"afterEach": false,
"before": false,
"beforeEach": false,
"describe": false,
"expect": false,
"inject": false,
"it": false,
"spyOn": false,
// functional test constants
"browser": false,
"by": false,
"element": false
}
}

605
Gruntfile.js Normal file
View File

@ -0,0 +1,605 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* This file serves as task declaration/configuration for steps executed
* during the grunt build. It loads grunt modules declared in package.json,
* configures them, and makes them available via 'grunt PLUGIN' on the
* commandline. It also groups these tasks into individual steps helpful during
* development, such as build, package, test, and release.
*
* @author Michael Krotscheck
*/
var config = {
livereload: {
port: 35729
}
};
var lrSnippet = require('connect-livereload')(config.livereload);
var mountFolder = function (connect, dir) {
'use strict';
return connect.static(require('path').resolve(dir));
};
module.exports = function (grunt) {
'use strict';
var dir = {
source : './src',
test : './test',
output : './dist',
report : './reports',
bower: './bower_components'
};
// load all grunt tasks
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
grunt.initConfig({
/**
* grunt clean
*
* Cleans our output directories.
*/
clean: {
dist: {
files: [
{
dot: true,
src: [
dir.report,
'./cover',
dir.output
]
}
]
}
},
/**
* grunt jshint
*
* Runs the JSHint linter against all the javascript files in our
* project, using the .jshintrc file shared with our IDE (sublime,
* eclipse, intellij, etc)
*/
jshint: {
options: {
jshintrc: '.jshintrc'
},
all: [
dir.source + '/**/*.js',
dir.test + '/**/*.js',
'./*.js'
]
},
/**
* grunt concat
*
* Creates a single file out of our javascript source in accordance
* with the concatenation priority. First the application module, then
* any dependent module declarations, and finally everything else.
*/
concat: {
dist: {
src: [
dir.source + '/app/storyboard.js',
dir.source + '/app/**/module.js',
dir.source + '/app/**/*.js'
],
dest: dir.output + '/js/storyboard.js'
}
},
/**
* grunt recess
*
* Compiles our .less CSS files into real CSS, linting as it goes. We
* do this manually during our build process so that we can inject
* our own variables into bootstrap and/or other CSS frameworks.
*
* Note: We're using LessCSS here because SASS requires ruby-compass,
* and cannot be easily installed with npm.
*/
recess: {
options: {
compile: true
},
bootstrap: {
src: [
dir.bower + '/bootstrap/less/bootstrap.less'
],
dest: dir.output + '/styles/bootstrap.css'
},
theme: {
src: [
dir.source + '/styles/**/*.less'
],
dest: dir.output + '/styles/theme.css'
}
},
/**
* grunt imagemin
*
* Runs optimizations on our images and copies them to the dist
* directory.
*/
imagemin: {
dist: {
files: [
{
expand: true,
cwd: dir.source + '/images',
src: '**/*.{png,jpg,jpeg}',
dest: dir.output + '/images'
}
]
}
},
/**
* grunt html2js
*
* A convenience method that converts all of the templates found in our
* project into a single javascript file (mostly by converting them
* into strings). This presents a tradeoff: All of the HTML layout
* is loaded up front, which could take a while, but it prevents load
* lag during application runtime.
*/
html2js: {
options: {
module: 'sb.templates',
base: dir.source
},
main: {
src: [dir.source + '/app/templates/**/*.html'],
dest: dir.output + '/js/templates.js'
}
},
/**
* grunt copy
*
* Copies any as-yet-unprocessed files into the dist directory, as well
* as pulling any assets from imported libraries to their appropriate
* locations.
*/
copy: {
dist: {
files: [
{
expand: true,
dot: true,
cwd: dir.source,
dest: dir.output,
src: [
'**/*.{ico,txt,eot,ttf,woff}',
'*.html',
'robots.txt'
]
},
{
expand: true,
dot: true,
cwd: dir.bower + '/font-awesome',
dest: dir.output,
src: [
'fonts/*.*'
]
}
]
}
},
/**
* @private
*
* grunt useminPrepare
*
* This task is a configuration builder, used to parse minification
* annotations in index.html. It's used in our compile step as an
* dependency concatenator, so that we can easily declare what we
* need in the index.html file and get all the pieces wrapped up
* nice and pretty by the script.
*
* It will generate configurations for the concat task (the others
* are explicitly disabled).
*/
useminPrepare: {
html: [dir.source + '/index.html'],
options: {
flow: {
steps: {
'js': ['concat'],
'css': ['concat']
},
post: []
},
dest: dir.output
}
},
/**
* grunt useminPrepare cssmin
*
* Parses all of the css references in our index.html and minifies them.
* The configuration for this task is generated by useminPrepare.
*/
cssmin: {
minify: {
expand: true,
cwd: dir.output + '/styles/',
src: ['*.css'],
dest: dir.output + '/styles/'
}
},
/**
* grunt useminPrepare uglify
*
* Performs a minifcation on our concatenated javascript, making sure
* not to mangle angularjs' injector pattern.
*/
uglify: {
options: {
mangle: false
},
dist: {
files: [
{
expand: true,
cwd: dir.output + '/js',
src: '**/*.js',
dest: dir.output + '/js'
}
]
}
},
/**
* grunt useminPrepare usemin
*
* Completes the packaging task by renaming all modified asset
* references from the previous steps in referencing documents.
*/
usemin: {
html: [
dir.output + '/index.html'
],
css: [
dir.output + '/styles/**/*.css'
],
options: {
dirs: [dir.output]
}
},
/**
* grunt htmlmin
*
* The final optimization step, which cleans up our html file and
* removes extraneous comments, tags, and more.
*/
htmlmin: {
dist: {
options: {
removeComments: true,
removeCommentsFromCDATA: true,
collapseWhitespace: false,
collapseBooleanAttributes: false,
removeAttributeQuotes: false,
removeRedundantAttributes: false,
useShortDoctype: false,
removeEmptyAttributes: true,
removeOptionalTags: true
},
files: [
{
expand: true,
cwd: dir.output,
src: ['index.html'],
dest: dir.output
}
]
}
},
/**
* grunt open
*
* Opens your default web browser to the specified URL. This is mostly
* used when running server, so that the developer doesn't have to know
* what URL/port the dev box is running on.
*/
open: {
server: {
url: 'http://localhost:<%= connect.options.port %>'
}
},
/**
* grunt watch
*
* This task is run with grunt server, in order to automatically update
* the hosted files that are served via the devserver. The livereload
* directive will then communicate with the browser and refresh the page
* when necessary.
*/
watch: {
concat: {
files: [
dir.source + '/app/storyboard.js',
dir.source + '/app/**/module.js',
dir.source + '/app/**/*.js'
],
tasks: ['concat']
},
recess: {
files: [
dir.source + '/styles/**/*.less'
],
tasks: ['recess:bootstrap', 'recess:theme']
},
copy: {
files: [
dir.source + '/**/*.{ico,txt,eot,ttf,woff}'
],
tasks: ['copy']
},
index: {
files: [
dir.source + '/index.html'
],
tasks: ['compile']
},
templates: {
files: [
dir.source + '/app/templates/**/*.html'
],
tasks: ['html2js']
},
jshint: {
files: [
'Gruntfile.js',
dir.source + '/**/*.js',
dir.test + '/**/*.js'
],
tasks: ['jshint']
},
livereload: {
options: {
livereload: config.livereload.port
},
files: [
dir.output + '/**/*.*'
]
}
},
/**
* grunt connect
*
* The connect plugin hosts a simple web server with our application,
* either under development or under test.
*/
connect: {
options: {
port: 9000,
hostname: 'localhost'
},
livereload: {
options: {
middleware: function (connect) {
return [
lrSnippet,
mountFolder(connect, dir.output)
];
}
}
},
dist: {
options: {
keepalive: true,
middleware: function (connect) {
return [
mountFolder(connect, dir.output)
];
}
}
},
test: {
options: {
middleware: function (connect) {
return [
mountFolder(connect, dir.output)
];
}
}
}
},
/**
* grunt karma:unit / grunt karma:integration
*
* This task runs the unit or integration suite on the compiled code.
*/
karma: {
unit: {
configFile: './karma-unit.conf.js'
},
integration: {
configFile: './karma-integration.conf.js'
}
},
/**
* grunt shell:xvfbStart / grunt shell:xvfbStop
*
* Starts and stops a virtual frame buffer.
*/
shell: {
xvfbStart: {
command: 'source ./bin/xvfb.sh start'
},
xvfbStop: {
command: 'source ./bin/xvfb.sh stop'
}
},
/**
* grunt protractor
*
* Protractor is an angular-provided method by which jasmine tests are
* executed via the selenium web driver. Its goal is to handle browser
* drive testing, rather than unit or integration testing.
*/
protractor: {
options: {
configFile: './protractor.conf.js',
keepAlive: true,
noColor: false,
args: {
}
},
dist: {}
}
});
/**
* Compiles all of our sources.
*/
grunt.registerTask('compile', [
'jshint',
'useminPrepare',
'concat',
'recess',
'imagemin',
'html2js',
'copy',
'usemin'
]);
/**
* Package built code into a release package.
*/
grunt.registerTask('package', [
'uglify',
'cssmin',
'htmlmin'
]);
/**
* Compile and packages our code.
*/
grunt.registerTask('build', [
'compile',
'package'
]);
/**
* This task performs a full build of our application, and then runs that
* source in a local web server. It does no watching, it simply hosts the
* files.
*/
grunt.registerTask('server:dist', [
'clean',
'compile',
'package',
'open',
'connect:dist'
]);
/**
* Development server - runs a build and sets up concurrent watchers that
* will automatically lint, test, and refresh
* the code when a change is detected.
*/
grunt.registerTask('server', [
'clean',
'compile',
'connect:livereload',
'open',
'watch'
]);
/**
* grunt test:integration
*
* This command will create a clean build against which our unit
* tests will be run. For more information, please see
* karma-unit.conf.js
*/
grunt.registerTask('test:unit', [
'clean',
'compile',
'useminPrepare',
'concat',
'karma:unit'
]);
/**
* grunt test:integration
*
* This command will create a clean build against which our integration
* tests will be run. For more information, please see
* karma-integration.conf.js
*/
grunt.registerTask('test:integration', [
'clean',
'compile',
'useminPrepare',
'concat',
'karma:integration'
]);
/**
* grunt test:functional
*
* This command will create a clean build against which our functional
* tests will be run. For more information, please see
* karma-functional.conf.js
*/
grunt.registerTask('test:functional', [
'clean',
'compile',
'connect:test',
'protractor'
]);
/**
* grunt test
*
* Run all the tests.
*/
grunt.registerTask('test', [
'clean',
'compile',
'useminPrepare',
'concat',
'karma:unit',
'karma:integration',
'package',
'connect:test',
'protractor'
]);
};

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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.

37
README.md Normal file
View File

@ -0,0 +1,37 @@
storyboard-webclient
====================
A PoC WebClient for the OpenStack Storyboard project.
### Prerequisites: Quick build/CI
* Xvfb
* GCC 4.2 or newer
* Python 2.6 or 2.7
* GNU Make 3.81 or newer
* libexecinfo (FreeBSD and OpenBSD only)
### Prerequisites: Dev
* NodeJS 0.10.24 or newer
* Grunt 0.4.2
* bower 1.2.8
### Command reference:
**Bootstrap & build the CI environment**
* `./bin/bootstrap.sh`
* `./bin/build.sh`
**Run a local development server**
`grunt server`
**Run the test suite**
`grunt test`
**Package the distro**
`grunt build`

71
bin/bootstrap.sh Executable file
View File

@ -0,0 +1,71 @@
#!/bin/bash -xe
# This script bootstraps the current workspace with a locally compiled
# node/grunt/bower javascript toolchain. This is done because recent NodeJS
# releases (v0.10+) are not available for the images we use for builds
# (CentOS, Ubuntu 12.04 precise), and because we only need node to generate our
# static assets.
#
node_version=0.10.24
script_dir="$( cd "$( dirname "$0" )" && pwd )"
workspace_path="$(dirname "$script_dir")"
node_archive_path=~/.cache/storyboard/node-v$node_version.tar.gz
node_remote_path=http://nodejs.org/dist/v$node_version/node-v$node_version.tar.gz
# Sanity check cleanup.
rm -fr $workspace_path/.local/
rm -fr $workspace_path/.build/
# Create our working directories
mkdir -p $workspace_path/.local/
mkdir -p $workspace_path/.build/
mkdir -p ~/.cache/storyboard
# Download the source if we don't have it already.
if [ ! -f $node_archive_path ]; then
echo "Downloading Node v$node_version..."
cd ~/.cache/storyboard
wget $node_remote_path -O $node_archive_path
cd $workspace_path
fi
# Compile into the workspace, so we keep things isolated.
# Note that on build nodes without ccache this will take a while.
cd $workspace_path/.build/
tar -xf $node_archive_path
cd $workspace_path/.build/node-v$node_version
# Run config, exit & dump if it fails.
echo 'Configuring...'
CONFIG_OUTPUT=$(./configure --prefix=$workspace_path/.local/ 2>&1)
if [ $? != 0 ]; then
echo $CONFIG_OUTPUT
cd $workspace_path
exit 1
fi
# Run make
echo 'Make...'
MAKE_OUTPUT=$(make 2>&1)
if [ $? != 0 ]; then
echo $MAKE_OUTPUT
cd $workspace_path
exit 1
fi
# Run make install
echo 'Make Install...'
MAKE_INSTALL_OUTPUT=$(make install 2>&1)
if [ $? != 0 ]; then
echo $MAKE_INSTALL_OUTPUT
cd $workspace_path
exit 1
fi
# Go back home...
cd $workspace_path
exit 0

38
bin/build.sh Executable file
View File

@ -0,0 +1,38 @@
#!/bin/bash
# This script executes the build.
VDISPLAY=99
DIMENSIONS='1280x1024x24'
XVFB=/usr/bin/Xvfb
BIN_DIR="$( cd "$( dirname "$0" )" && pwd )"
WORKSPACE="$(dirname "$BIN_DIR")"
# Add our new bin directory to the PATH
echo "Adding $WORKSPACE/.local/bin to PATH"
export PATH=$WORKSPACE/.local/bin:$PATH
echo "Adding $WORKSPACE/node_modules/.bin to PATH"
export PATH=$WORKSPACE/node_modules/.bin:$PATH
cd $WORKSPACE;
echo "Installing build dependencies"
npm prune
npm install
echo "Installing compile dependencies"
bower prune
bower install
echo "Launching Virtual Frame Buffer"
$XVFB :${VDISPLAY} -screen 0 ${DIMENSIONS} -ac +extension GLX +render -noreset 2>&1 > /dev/null &
echo "Building"
set +e
DISPLAY=:${VDISPLAY} grunt clean test
result=$?
pkill Xvfb 2>&1 > /dev/null
set -e
exit $result

22
bower.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "storyboard-webclient",
"version": "0.0.1",
"dependencies": {
"jquery": "2.0.3",
"font-awesome": "4.0",
"angular": "1.2.5",
"angular-resource": "1.2.5",
"angular-cookies": "1.2.5",
"angular-sanitize": "1.2.5",
"bootstrap": "3.0.0",
"angular-ui-router": "0.2.0",
"angular-translate": "1.1.1"
},
"devDependencies": {
"angular-mocks": "1.2.5",
"angular-scenario": "1.2.5"
},
"resolutions": {
"angular": "1.2.5"
}
}

66
karma-integration.conf.js Normal file
View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
module.exports = function (config) {
'use strict';
config.set({
basePath: '',
frameworks: ['jasmine'],
plugins: [
'karma-coverage',
'karma-jasmine',
'karma-html-reporter',
'karma-phantomjs-launcher',
'karma-chrome-launcher',
'karma-firefox-launcher'
],
files: [
'./dist/js/*.js',
'./test/unit/**/*.js'
],
exclude: [
],
singleRun: true,
reporters: ['dots', 'progress', 'coverage', 'html'],
colors: false,
browsers: [ 'PhantomJS', 'Firefox' ],
preprocessors: {
'./dist/js/storyboard.js': ['coverage']
},
coverageReporter: {
type: 'html',
dir: './cover/integration/'
},
htmlReporter: {
outputDir: './reports/integration',
templatePath: './node_modules' +
'/karma-html-reporter/jasmine_template.html'
}
});
};

66
karma-unit.conf.js Normal file
View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
module.exports = function (config) {
'use strict';
config.set({
basePath: '',
frameworks: ['jasmine'],
plugins: [
'karma-coverage',
'karma-jasmine',
'karma-html-reporter',
'karma-phantomjs-launcher',
'karma-chrome-launcher',
'karma-firefox-launcher'
],
files: [
'./dist/js/*.js',
'./test/unit/**/*.js'
],
exclude: [
],
singleRun: true,
reporters: ['dots', 'progress', 'coverage', 'html'],
colors: false,
browsers: [ 'PhantomJS', 'Firefox' ],
preprocessors: {
'./dist/js/storyboard.js': ['coverage']
},
coverageReporter: {
type: 'html',
dir: './cover/unit/'
},
htmlReporter: {
outputDir: './reports/unit',
templatePath: './node_modules' +
'/karma-html-reporter/jasmine_template.html'
}
});
};

57
package.json Normal file
View File

@ -0,0 +1,57 @@
{
"name": "storyboard-webclient",
"version": "0.0.1",
"description": "An all-javascript webclient for the Storyboard API",
"main": "index.html",
"scripts": {
"test": "grunt test"
},
"repository": "",
"keywords": [
"openstack",
"storyboard",
"task",
"project management"
],
"author": "Michael Krotscheck",
"license": "Apache2",
"devDependencies": {
"connect-livereload": "0.3.1",
"karma-jasmine": "0.1.4",
"grunt-contrib-concat": "0.3.0",
"grunt-contrib-copy": "0.4.1",
"grunt-contrib-clean": "0.5.0",
"grunt-html2js": "0.2.3",
"grunt-open": "0.2.2",
"grunt-usemin": "2.0.2",
"grunt-contrib-htmlmin": "0.1.3",
"grunt-contrib-cssmin": "0.7.0",
"grunt-karma": "0.6.2",
"grunt-contrib-connect": "0.5.0",
"grunt-contrib-watch": "0.5.3",
"grunt-contrib-jshint": "0.7.2",
"grunt-contrib-uglify": "0.2.7",
"grunt-contrib-imagemin": "0.4.0",
"grunt-recess": "0.5.0",
"grunt": "0.4.2",
"grunt-cli": "0.1.11",
"matchdep": "0.1.2",
"karma-script-launcher": "0.1.0",
"karma-chrome-launcher": "0.1.1",
"karma-html2js-preprocessor": "0.1.0",
"karma-firefox-launcher": "0.1.2",
"karma-coffee-preprocessor": "0.1.1",
"requirejs": "2.1.9",
"karma-requirejs": "0.2.0",
"karma-phantomjs-launcher": "0.1.1",
"karma": "0.10.8",
"bower": "1.2.8",
"grunt-shell": "0.6.1",
"karma-coverage": "0.1.4",
"grunt-env": "0.4.1",
"protractor": "0.15.0",
"grunt-protractor-runner": "0.2.0",
"selenium-standalone": "2.39.0-2.7.0",
"karma-html-reporter": "~0.1.1"
}
}

97
protractor.conf.js Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
// A reference configuration file.
exports.config = {
seleniumServerJar: './node_modules/selenium-standalone/' +
'.selenium/2.39.0/server.jar',
chromeDriver: './node_modules/selenium-standalone/' +
'.selenium/2.39.0/chromedriver',
chromeOnly: false,
// Additional command line options to pass to selenium. For example,
// if you need to change the browser timeout, use
// seleniumArgs: ['-browserTimeout=60'],
seleniumArgs: [],
// The timeout for each script run on the browser. This should be longer
// than the maximum time your application needs to stabilize between tasks.
allScriptsTimeout: 11000,
// ----- What tests to run -----
//
// Spec patterns are relative to the location of this config.
specs: [
'./test/functional/**/*.js'
],
// ----- Capabilities to be passed to the webdriver instance ----
//
// For a full list of available capabilities, see
// https://code.google.com/p/selenium/wiki/DesiredCapabilities
capabilities: {
'browserName': 'firefox'
},
// ----- More information for your tests ----
//
// A base URL for your application under test. Calls to protractor.get()
// with relative paths will be prepended with this.
baseUrl: 'http://localhost:9000',
// Selector for the element housing the angular app - this defaults to
// body, but is necessary if ng-app is on a descendant of <body>
rootElement: 'html',
// A callback function called once protractor is ready and available, and
// before the specs are executed
// You can specify a file containing code to run by setting onPrepare to
// the filename string.
onPrepare: function () {
// At this point, global 'protractor' object will be set up, and jasmine
// will be available. For example, you can add a Jasmine reporter with:
// jasmine.getEnv().addReporter(new jasmine.JUnitXmlReporter(
// 'outputdir/', true, true));
},
// The params object will be passed directly to the protractor instance,
// and can be accessed from your test. It is an arbitrary object and can
// contain anything you may need in your test.
// This can be changed via the command line as:
// --params.login.user 'Joe'
params: {
login: {
user: 'Jane',
password: '1234'
}
},
// ----- Options to be passed to minijasminenode -----
//
// See the full list at https://github.com/juliemr/minijasminenode
jasmineNodeOpts: {
// onComplete will be called just before the driver quits.
onComplete: null,
// If true, display spec names.
isVerbose: false,
// If true, print colors to the terminal.
showColors: true,
// If true, include stack traces in failures.
includeStackTrace: true,
// Default time to wait in ms before a test fails.
defaultTimeoutInterval: 30000
}
};

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* This controller handles the logic for the authorization provider list page.
*
* @author Michael Krotscheck
*/
angular.module('sb.auth').controller('AuthListController',
function ($scope, authProviders, $state) {
'use strict';
// If there's only one auth provider, just use that.
if (!!authProviders && authProviders.length === 1) {
$state.go('auth.provider.id', {id: authProviders[0].id});
}
$scope.authProviders = authProviders;
});

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* This controller handles the logic for the authorization provider list page.
*
* @author Michael Krotscheck
*/
angular.module('sb.auth').controller('AuthLoginController',
function ($scope, authProvider) {
'use strict';
$scope.authProvider = authProvider;
});

62
src/app/auth/module.js Normal file
View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* This Storyboard module contains our adaptive authentication and authorization
* logic.
*
* @author Michael Krotscheck
*/
angular.module('sb.auth',
[ 'sb.services', 'sb.templates', 'ui.router']
)
.config(function ($stateProvider, $urlRouterProvider,
AuthProviderResolver) {
'use strict';
// Default rerouting.
$urlRouterProvider.when('/auth', '/auth/provider/list');
$urlRouterProvider.when('/auth/provider', '/auth/provider/list');
// Declare the states for this module.
$stateProvider
.state('auth', {
abstract: true,
url: '/auth',
template: '<div ui-view></div>'
})
.state('auth.provider', {
abstract: true,
url: '/provider',
template: '<div ui-view></div>'
})
.state('auth.provider.list', {
url: '/list',
templateUrl: 'app/templates/auth/provider/list.html',
controller: 'AuthListController',
resolve: {
authProviders: AuthProviderResolver.resolveAuthProviders
}
})
.state('auth.provider.id', {
url: '/:id',
templateUrl: 'app/templates/auth/provider/login.html',
controller: 'AuthLoginController',
resolve: {
authProvider: AuthProviderResolver.resolveAuthProvider('id')
}
});
});

40
src/app/pages/module.js Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* The Storyboard pages submodule contains mostly static content pages that
* require little functionality themselves.
*
* @author Michael Krotscheck
*/
angular.module('sb.pages',
[ 'sb.services', 'sb.templates', 'sb.pages', 'ui.router']
)
.config(function ($stateProvider) {
'use strict';
// Set our page routes.
$stateProvider
.state('page', {
abstract: true,
url: '/page',
template: '<div ui-view></div>'
})
.state('page.about', {
url: '/about',
templateUrl: 'app/templates/page/about.html'
});
});

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* The Storyboard project submodule handles most activity surrounding the
* creation and management of projects.
*/
angular.module('sb.projects').controller('ProjectListController',
function ($scope) {
'use strict';
$scope.search = function () {
};
});

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* The Storyboard project submodule handles most activity surrounding the
* creation and management of projects.
*
* @author Michael Krotscheck
*/
angular.module('sb.projects', ['ui.router', 'sb.services'])
.config(function ($stateProvider, $urlRouterProvider) {
'use strict';
// URL Defaults.
$urlRouterProvider.when('/project', '/project/list');
// Set our page routes.
$stateProvider
.state('project', {
abstract: true,
url: '/project',
template: '<div ui-view></div>'
})
.state('project.list', {
url: '/list',
templateUrl: 'app/templates/project/provider.html'
});
});

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* An HTTP request interceptor that broadcasts response status codes to the
* rest of the application as events. These events are broadcast before the
* error response itself is passed back to the receiving closure, so please
* keep that in mind as you base your application logic on it.
*
* @author Michael Krotscheck
*/
angular.module('sb.services')
// Create an HTTP Error Broadcaster that intercepts requests and lets the
// rest of the application know about what happened.
.factory('httpErrorBroadcaster', function ($q, $rootScope) {
'use strict';
function sendEvent(status, body) {
// Only send an event if a status is passed.
if (!!status) {
$rootScope.$broadcast('http_' + status, body || {});
}
}
return {
/**
* Handle a success response.
*/
response: function (response) {
if (!!response) {
sendEvent(response.status);
}
return response;
},
/**
* Handle a fail response.
*/
responseError: function (response) {
if (!!response) {
sendEvent(response.status, response.body);
}
return $q.reject(response);
}
};
})
// Attach the HTTP interceptor.
.config(function ($httpProvider) {
'use strict';
$httpProvider.interceptors.unshift('httpErrorBroadcaster');
});

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* The Storyboard Services module contains all of the necessary API resources
* used by the storyboard client. Its resources are available via injection to
* any module that declares it as a dependency.
*
* @author Michael Krotscheck
*/
angular.module('sb.services', ['ngResource', 'ngCookies', 'ngMockE2E']);

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* This provider attempts to discover the API URI base for storyboard, by
* checking various expected configuration parameters.
*
* @author Michael Krotscheck
*/
angular.module('sb.services')
.config(function ($provide, $injector) {
'use strict';
var propertyName = 'storyboardApiBase';
// First to see whether something's already been injected.
if ($injector.has(propertyName)) {
// We've already got one, exit.
return;
}
// Do we have a global ENV property with something we can use?
if (window.hasOwnProperty('ENV')) {
var ENV = window.ENV;
if (ENV !== null && ENV.hasOwnProperty(propertyName)) {
$provide.constant(propertyName, ENV[propertyName]);
return;
}
}
// If there is a <base> tag, then we can use that.
if ($('base').length > 0) {
$provide.constant(propertyName, '');
return;
}
// Neither of those work, so default to something sane on the current
// domain
$provide.constant(propertyName, '/api/v1');
});

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* This collection of utility methods allow us to pre-resolve AuthProvider
* resources before a UI route switch is completed.
*
* @author Michael Krotscheck
*/
angular.module('sb.services').constant('AuthProviderResolver', {
/**
* Resolves all available authorization providers.
*/
resolveAuthProviders: function ($q, AuthProvider, $log) {
'use strict';
$log.debug('Resolving AuthProviders');
var deferred = $q.defer();
AuthProvider.query(
function (result) {
deferred.resolve(result);
},
function (error) {
$log.warn('Route resolution rejected for AuthProviders');
deferred.reject(error);
});
return deferred.promise;
},
/**
* Resolves an AuthProvider based on the unique ID passed via the
* stateParams.
*/
resolveAuthProvider: function (stateParamName) {
'use strict';
return function ($q, AuthProvider, $stateParams, $log) {
var deferred = $q.defer();
if (!$stateParams.hasOwnProperty(stateParamName)) {
$log.warn('State did not contain property of name ' +
stateParamName);
deferred.reject({
'error': true
});
} else {
var id = $stateParams[stateParamName];
$log.debug('Resolving AuthProvider: ' + id);
AuthProvider.get({'id': id},
function (result) {
deferred.resolve(result);
},
function (error) {
$log.warn('Route resolution rejected for ' +
'AuthProvider ' + id);
deferred.reject(error);
});
return deferred.promise;
}
};
}
});

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* This resource exposes authorization providers to our angularjs environment,
* allowing us to manage & control them. It's also used during the
* authorization/login process to determine how we're going to allow users to
* log in to storyboard.
*
* @author Michael Krotscheck
*/
angular.module('sb.services').factory('AuthProvider',
function ($resource, storyboardApiBase) {
'use strict';
return $resource(storyboardApiBase + '/auth/provider/:id',
{id: '@id'},
{
'create': {
method: 'POST'
},
'get': {
method: 'GET',
cache: true
},
'save': {
method: 'PUT'
},
'delete': {
method: 'DELETE'
},
'query': {
method: 'GET',
isArray: true,
transformResponse: function (data) {
if (data.error) {
return data;
} else {
return data.results;
}
}
}
});
});

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* Mock resource responses for the AuthProvider resource.
*
* @author Michael Krotscheck
*/
angular.module('sb.services')
.run(function ($httpBackend, $injector) {
'use strict';
$httpBackend = $injector.get('$httpBackend');
var authProviders = [
{
'id': 1,
'type': 'openid',
'title': 'OpenID',
'url': 'https://www.google.com/prediscovered' +
'/redirection/url',
'params': {
'list': 'of',
'additional': 'parameters',
'required': 'for.this.provider'
}
},
{
'id': 2,
'type': 'openid_connect',
'title': 'OpenID Connect',
'url': 'https://www.google.com/prediscovered' +
'/redirection/url',
'params': {
'list': 'of',
'additional': 'parameters',
'required': 'for.this.provider'
}
},
{
'id': 3,
'type': 'ldap',
'title': 'LDAP',
'url': 'https://www.google.com/prediscovered' +
'/redirection/url',
'params': {
'list': 'of',
'additional': 'parameters',
'required': 'for.this.provider'
}
}
];
$httpBackend.when('GET', '/api/v1/auth/provider')
.respond(
{
total: 1,
offset: 0,
limit: 10,
results: authProviders
}
);
$httpBackend.when('GET', '/api/v1/auth/provider/1')
.respond(authProviders[0]);
$httpBackend.when('GET', '/api/v1/auth/provider/2')
.respond(authProviders[1]);
$httpBackend.when('GET', '/api/v1/auth/provider/3')
.respond(authProviders[2]);
});

59
src/app/storyboard.js Normal file
View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* The Storyboard root application module.
*
* This module contains the entire, standalone application for the Storyboard
* ticket tracking web client.
*
* @author Michael Krotscheck
*/
angular.module('storyboard',
[ 'sb.services', 'sb.templates', 'sb.pages', 'sb.projects', 'sb.auth',
'ui.router']
)
.config(function ($provide, $stateProvider, $urlRouterProvider,
$locationProvider, $httpProvider) {
'use strict';
// Default URL hashbang route
$urlRouterProvider.otherwise('/');
// Override the hash prefix for Google's AJAX crawling.
$locationProvider.hashPrefix('!');
// Set an intial home page.
$stateProvider
.state('index', {
url: '/',
templateUrl: 'app/templates/index.html'
});
// Attach common request headers out of courtesy to the API
$httpProvider.defaults.headers.common['X-Client'] = 'Storyboard';
})
.run(function ($log, $rootScope, $location) {
'use strict';
// Listen to changes on the root scope. If it's an error in the state
// changes (i.e. a 404) take the user back to the index.
$rootScope.$on('$stateChangeError',
function () {
$location.path('/');
});
});

View File

@ -0,0 +1,33 @@
<!--
~ Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
~
~ 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.
-->
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>How would you like to log in?</h1>
<hr/>
</div>
<div class="col-sm-8 col-xs-12">
<a ng-repeat="provider in authProviders"
ng-class="[provider.type]"
class="auth-provider btn btn-info btn-lg btn-block"
href="#!/auth/provider/{{provider.id}}">
<i class="fa fa-caret-right"></i> {{provider.title}}
</a>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
<!--
~ Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
~
~ 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.
-->
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>Login with {{authProvider.title}}</h1>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
<!--
~ Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
~
~ 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.
-->
<div class="row text-muted">
<hr/>
<small class="pull-right">
Powered by Storyboard |
<a href="#!/page/about">About</a>
</small>
</div>

View File

@ -0,0 +1,55 @@
<!--
~ Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
~
~ 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.
-->
<div class="container">
<div class="navbar-header">
<button class="navbar-toggle" type="button" data-toggle="collapse"
data-target=".sb-navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="#!/" class="navbar-brand">
<i class="icon icon_openstack"></i> Openstack
</a>
</div>
<nav class="collapse navbar-collapse sb-navbar-collapse" role="navigation">
<ul class="nav navbar-nav">
<li>
<a href="#!/project">Projects</a>
</li>
<li>
<a href="#!/stories">Stories</a>
</li>
<li>
<a href="#!/page/about">About</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li ng-show="isLoggedIn">
<a href="#!/profile">{{currentUser.firstName}}
{{currentUser.lastName}} <i class="fa fa-cog"></i></a>
</li>
<li ng-show="isLoggedIn">
<a href="#!/logout">Log out</a>
</li>
<li ng-hide="isLoggedIn">
<a href="#!/auth">Log in</a>
</li>
</li>
</ul>
</nav>
</div>

View File

@ -0,0 +1,67 @@
<!--
~ Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
~
~ 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.
-->
<div class="container">
<div class="row">
<div class="col-sm-12 jumbotron">
<h1>Storyboard</h1>
<p class="lead">A task tracking system for inter-related
projects.</p>
<p>StoryBoard lets you track what needs to be done across projects
and branches.
It is a proof-of-concept demo of what the ideal OpenStack task
tracker would
look like. It may or may not end up replacing Launchpad
Bugs/Blueprints for OpenStack task tracking and release
management.</p>
</div>
</div>
<div class="row">
<div class="col-sm-4">
<h2>Stories</h2>
<p>It all begins with a <strong>story</strong>. A story is a bug
report or proposed feature. Stories are then further split into
<strong>tasks</strong>, which affect a given project and branch.
You can easily track backports of bugs to a specific branch, or
plan cross-project features.</p>
<a href="#!/stories" class="btn btn-info">Access Stories
<i class="fa fa-chevron-right"></i> </a></div>
<div class="col-sm-4">
<h2>Projects</h2>
<p>StoryBoard lets you efficiently track your work across a large
number of interrelated projects. Flexible <strong>project
groups</strong> lets you get the views that makes the most
sense to you.</p>
<a href="#!/projects" class="btn btn-info">Access Projects
<i class="fa fa-chevron-right"></i> </a>
</div>
<div class="col-sm-4">
<h2>But why?</h2>
<p>The OpenStack project is now running into a number of limitations
and annoying differences in workflow with Launchpad. At the same
time, Launchpad development stalled, leaving us with little
chances to improve the tool to suit our needs. This POC reuses
key Launchpad concepts (like bug tasks) and goes beyond.</p>
<a href="https://github.com/ttx/storyboard/blob/master/README.rst"
class="btn btn-info">See project README
<i class="fa fa-chevron-right"></i> </a></div>
</div>
</div>

View File

@ -0,0 +1,95 @@
<!--
~ Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
~
~ 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.
-->
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>About Storyboard</h1>
</div>
</div>
<div class="row">
<div class="col-md-8 col-sm-12">
<p>Waxy gurn trimmed Leonine, waxy gurn 118 118 trimmed john cleese
theodore roosevelt charity donate socially mobile cigars Leonine
lando calrissian, john cleese testosterone trophy waxy gurn
musketeer lando calrissian helllloooo charming villain cigars
socially mobile 118 118 charity donate theodore roosevelt comb
Leonine trimmed.
</p>
<img class="img-thumbnail hidden-sm hidden-xs pull-right"
src="http://placekitten.com/300/300"/>
<p>
Old west sheriff comb soup strainer dont panic en time-warped
cabbie, rugged et tudor philosopher comb dont panic en
time-warped cabbie old west sheriff admiral soup strainer,
rugged et bruce forsyth clone zone shopper super mario comb old
west sheriff dont panic en time-warped cabbie groomed country
baron admiral tudor philosopher soup strainer.
</p>
<p>
Wario et sodales cum mustachioed wario hairy lipsum, holiday
waiter bruce forsyth hairy lipsum dick van dyke wario theodore
roosevelt wario leader of men horseshoe brandy sportacus
mustachioed et sodales cum, sportacus albert einstein leader of
men dick van dyke et sodales cum brandy wario theodore roosevelt
furry facial friend jolly good show bruce forsyth horseshoe
hairy lipsum holiday waiter wario mustachioed? Rock n roll star
groucho-a-like borat beefeater fox hunting, borat beefeater
groucho-a-like ian rush fox hunting toothbrush robot moustache
rock n roll star yeoman farmer groomed, robot moustache tudor
philosopher wario yeoman farmer groomed groucho-a-like borat ian
rush toothbrush beefeater rock n roll star hungarian fox
hunting?
</p>
<p> Groucho marx mr frothy-top glorious facial hair mr
frothy-top albert einstein furry facial friend stiff upper lip.
Robert winston um yesbaby middle eastern despot brigadier
godlike sweat irrigator,, brigadier middle eastern despot sweat
irrigator, zap rowsdower jolly good show iron tache godlike
robert winston um yesbaby.</p>
</div>
<div class="col-md-4 hidden-sm hidden-xs">
<h4>Attribution</h4>
<p>This site was built with the help of the following openly
licensed projects.</p>
<p><strong>FontAwesome </strong></p>
<p><a href="http://fontawesome.io/" target="_blank">The iconic font
designed for Bootstrap</a></p>
<p><strong>AngularJS</strong></p>
<p><a href="http://angularjs.org/" target="_blank">Superheroic
JavaScript Framework</a></p>
<p><strong>Bootstrap</strong></p>
<p><a href="http://getbootstrap.com/" target="_blank">Mobile first
front-end framework</a></p>
</div>
<p></p>
</div>
</div>

View File

@ -0,0 +1,48 @@
<!--
~ Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
~
~ 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.
-->
<div class="container">
<div class="row">
<div class="col-sm-2">
<div class="well sidebar-nav">
<ul class="nav nav-list">
<li class="nav-header">Projects</li>
<li class="disabled"><a href="#">Search projects</a></li>
</ul>
</div>
<!--/.well -->
</div>
<div class="col-sm-10">
<h3>Projects</h3>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Name</th>
<th>Title</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* This directive requires ui-router, as it listens for events dispatched as
* a user navigates through the application, and adds the 'active' class to
* the bound element if the user's selected path matches the one configured.
*
* @author Michael Krotscheck
*/
angular.module('sb.util').directive('activePath',
function ($location, $rootScope) {
'use strict';
return {
link: function ($scope, element, attrs) {
var activePath = attrs.activePath;
function setActivePath() {
var isActive = activePath === $location.path();
element.toggleClass('active', isActive);
}
$scope.$on('$destroy',
$rootScope.$on('$stateChangeSuccess', setActivePath)
);
// INIT
setActivePath();
}
};
});

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* This directive adds the often sought, but never found, ng-enter directive.
* It intercepts keystrokes and will execute the bound method if that keystroke
* is the enter key.
*
* @author Michael Krotscheck
*/
angular.module('sb.util').directive('ngEnter', function () {
'use strict';
return function (scope, element, attrs) {
element.bind('keydown keypress', function (event) {
if (event.which === 13) {
scope.$apply(function () {
scope.$eval(attrs.ngEnter);
});
event.preventDefault();
}
});
};
});

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* A helpful development filter that will console.log out any value you bind
* to it in the DOM. You should probably only use this while debugging.
*
* @author Michael Krotscheck
*/
angular.module('sb.util').filter('debug',
function () {
'use strict';
return function (value) {
console.warn('DEBUG', value);
return value;
};
});

17
src/app/util/module.js Normal file
View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
angular.module('sb.util', ['ui.router']);

BIN
src/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
src/images/logo/logo_16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
src/images/logo/logo_32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
src/images/logo/logo_48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
src/images/logo/logo_64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

61
src/index.html Normal file
View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<!--
Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
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.
-->
<html id="ng-app" ng-app="storyboard">
<head>
<meta charset="utf-8">
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<title>Storyboard</title>
<!-- Google's Ajax Crawling: https://developers.google.com/webmasters/ajax-crawling -->
<meta name="fragment" content="!">
<!-- Icons -->
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" href="styles/bootstrap.css">
<!-- build:css(bower_components) /styles/libs.css -->
<link rel="stylesheet" href="font-awesome/css/font-awesome.css">
<!-- endbuild -->
<!-- build:js(bower_components) /js/libs.js -->
<script src="jquery/jquery.js"></script>
<script src="angular/angular.js"></script>
<script src="angular-ui-router/release/angular-ui-router.js"></script>
<script src="angular-resource/angular-resource.js"></script>
<script src="angular-mocks/angular-mocks.js"></script>
<script src="angular-cookies/angular-cookies.js"></script>
<script src="angular-sanitize/angular-sanitize.js"></script>
<script src="bootstrap/dist/js/bootstrap.js"></script>
<!-- endbuild -->
<link rel="stylesheet" href="styles/theme.css">
<script src="js/storyboard.js"></script>
<script src="js/templates.js"></script>
</head>
<body>
<header class="navbar navbar-inverse navbar-fixed-top"
ng-include src="'app/templates/header.html'">
</header>
<div ui-view></div>
<footer class="container" ng-include src="'app/templates/footer.html'">
</footer>
</body>
</html>

3
src/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# robotstxt.org
User-agent: *

24
src/styles/auth.less Normal file
View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* Styles specific to the auth pages.
*/
a.auth-provider.btn {
text-align: left;
padding-left: 10px;
padding-right: 10px;
}

View File

@ -0,0 +1,24 @@
@font-face {
font-family: custom_font_icons;
src: url(../fonts/custom_font_icons.eot);
src: url(../fonts/custom_font_icons.eot?#iefix) format("embedded-opentype"), url(../fonts/custom_font_icons.woff) format("woff"), url(../fonts/custom_font_icons.ttf) format("truetype");
font-weight: 400;
font-style: normal
}
.icon {
font-family: custom_font_icons;
display: inline-block;
font-weight: 400;
font-style: normal;
speak: none;
text-decoration: inherit;
text-transform: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale
}
.icon_openstack:before {
content: "\f100"
}

26
src/styles/main.less Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* Our main SCSS layout.
*/
body {
margin-top: 70px;
}
i.icon {
line-height: .5em;
}

27
test/functional/module.js Normal file
View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
describe('Storyboard Homepage', function () {
'use strict';
it('should have storyboard as the title', function () {
// Load the AngularJS homepage.
browser.get('http://localhost:9000');
var title = element(by.tagName('title'));
expect(title.getInnerHtml()).toEqual('Storyboard');
});
});

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
describe('sb.services', function () {
'use strict';
var module;
var dependencies = [];
var hasModule = function (module) {
return dependencies.indexOf(module) >= 0;
};
beforeEach(function () {
// Get module
module = angular.module('sb.services');
dependencies = module.requires;
});
it('should exist', function () {
expect(module).toBeTruthy();
});
it('should load cookies module', function () {
expect(hasModule('ngCookies')).toBeTruthy();
});
});

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
describe('sb.services', function () {
'use strict';
var module;
var dependencies = [];
var hasModule = function (module) {
return dependencies.indexOf(module) >= 0;
};
beforeEach(function () {
// Get module
module = angular.module('sb.services');
dependencies = module.requires;
});
it('should exist', function () {
expect(module).toBeTruthy();
});
it('should load cookies module', function () {
expect(hasModule('ngCookies')).toBeTruthy();
});
});