Grunt-based dependencies, minification, and unit testing
authorBill Erickson <berick@esilibrary.com>
Tue, 6 May 2014 16:24:01 +0000 (12:24 -0400)
committerBill Erickson <berick@esilibrary.com>
Tue, 6 May 2014 16:31:47 +0000 (12:31 -0400)
Files to support Grunt-based browser staff building.

* Fetching dependencies (angularjs, etc.)
* Runs unit tests with grunt-karma
* Concatenates and minifies files with grunt-uglify

* Moved unit tests into web directory
* Taught t_base_js.tt2 how to load the minified version of the file

TODO:

* minifiying non-core files and (possibly) copying them into the build
  directory.
* Support for using non-minified files via browser option?
* Makefile options for building, testing, and copying the correct files
  into the actual web directory (instead of cp -r web/*)
* Build docs

Signed-off-by: Bill Erickson <berick@esilibrary.com>
18 files changed:
Open-ILS/src/templates/staff/t_base_js.tt2
Open-ILS/tests/staffweb/LICENSE.angular-seed [deleted file]
Open-ILS/tests/staffweb/app/js [deleted symlink]
Open-ILS/tests/staffweb/config/karma.conf.js [deleted file]
Open-ILS/tests/staffweb/scripts/e2e-test.bat [deleted file]
Open-ILS/tests/staffweb/scripts/e2e-test.sh [deleted file]
Open-ILS/tests/staffweb/scripts/test.bat [deleted file]
Open-ILS/tests/staffweb/scripts/test.sh [deleted file]
Open-ILS/tests/staffweb/scripts/watchr.rb [deleted file]
Open-ILS/tests/staffweb/scripts/web-server.js [deleted file]
Open-ILS/tests/staffweb/test/unit/egHomeApp.js [deleted file]
Open-ILS/tests/staffweb/test/unit/egPatronApp.js [deleted file]
Open-ILS/tests/staffweb/update-angular.sh [deleted file]
Open-ILS/web/js/ui/default/staff/Gruntfile.js [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/package.json [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/test/karma.conf.js [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/test/unit/egHomeApp.js [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/test/unit/egPatronApp.js [new file with mode: 0644]

index 95b9786..2231801 100644 (file)
@@ -2,27 +2,26 @@
 <!-- TODO: combine and minify JS -->
 
 [% 
-  ANGULAR_VERSION = '1.2.16';
-  ANGULAR_BOOTSTRAP_VERSION = '0.9.0';
+  EG_VERSION = '0.0.1'; # FIXME: build optoin
+  EXPAND_JS_IMPORTS = 1; # FIXME: env option?
 %]
 
-<!-- hosted angular libs -->
-<script src="//ajax.googleapis.com/ajax/libs/angularjs/[% ANGULAR_VERSION %]/angular.min.js"></script>
-<script src="//ajax.googleapis.com/ajax/libs/angularjs/[% ANGULAR_VERSION %]/angular-route.min.js"></script>
-<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/[% ANGULAR_BOOTSTRAP_VERSION %]/ui-bootstrap-tpls.min.js"></script>
+<script src="/IDL2js"></script>
+
+[% IF EXPAND_JS_IMPORTS %]
+
+<!-- angular -->
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/build/angular.min.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/build/angular-route.min.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/build/ui-bootstrap.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/build/hotkeys.min.js"></script>
 
 <!-- IDL / opensrf (network) -->
-<script src="/IDL2js"></script>
 <script src="[% ctx.media_prefix %]/js/dojo/opensrf/JSON_v1.js"></script>
 <script src="[% ctx.media_prefix %]/js/dojo/opensrf/opensrf.js"></script>
 <script src="[% ctx.media_prefix %]/js/dojo/opensrf/opensrf_ws.js"></script>
-<script>
-  // pending api_level thunking in C
-  // OpenSRF.api_level = 2;
-  OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_WS;
-</script>
 
-<!-- angular-driven shared services -->
+<!-- evergreen core services -->
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/core.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/strings.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/idl.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/startup.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/hatch.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/coresvc.js"></script>
-
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/navbar.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/statusbar.js"></script>
-
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/ui.js"></script>
 
-<script src="[% ctx.media_prefix %]/js/ui/default/staff/navbar.js"></script>
+[% ELSE %]
+
+<!-- concatenated, minified version of all of the above -->
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/build/evergreen-staff-client.[% EG_VERSION %].min.js"></script>
+
+[% END %]
+
+<script>
+  // Configure OpenSRF
+  // pending api_level thunking in C
+  // OpenSRF.api_level = 2;
+  OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_WS;
+</script>
 
diff --git a/Open-ILS/tests/staffweb/LICENSE.angular-seed b/Open-ILS/tests/staffweb/LICENSE.angular-seed
deleted file mode 100644 (file)
index f2d3ff7..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-The MIT License
-
-Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
diff --git a/Open-ILS/tests/staffweb/app/js b/Open-ILS/tests/staffweb/app/js
deleted file mode 120000 (symlink)
index 7f89b88..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../../../web/js/ui/default/staff
\ No newline at end of file
diff --git a/Open-ILS/tests/staffweb/config/karma.conf.js b/Open-ILS/tests/staffweb/config/karma.conf.js
deleted file mode 100644 (file)
index 4c657f3..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-module.exports = function(config){
-    config.set({
-    basePath : '../',
-    //logLevel : config.LOG_DEBUG,
-
-    files : [
-      'app/lib/angular/angular.js',
-      'app/lib/angular/angular-*.js',
-      'app/lib/angular-ui-bootstrap/angular-bootstrap-min.js',
-      'test/lib/angular/angular-mocks.js',
-      'app/lib/opensrf/JSON_v1.js',
-      'app/lib/opensrf/md5.js',
-      'app/lib/opensrf/opensrf.js',
-      'app/lib/opensrf/opensrf_xhr.js',
-      'app/js/services/core.js',
-      'app/js/services/idl.js',
-      'app/js/services/event.js',
-      'app/js/services/net.js',
-      'app/js/services/auth.js',
-      'app/js/services/pcrud.js',
-      'app/js/services/env.js',
-      'app/js/services/org.js',
-      'app/js/services/user.js',
-      'app/js/services/startup.js',
-      'app/js/services/ui.js',
-      'app/js/**/*.js',
-      'test/unit/**/*.js'
-    ],
-
-    exclude : [
-      'app/lib/angular/angular-loader.js',
-      'app/lib/angular/*.min.js',
-      'app/lib/angular/angular-scenario.js'
-    ],
-
-    autoWatch : true,
-
-    frameworks: ['jasmine'],
-
-    browsers : ['Chrome'],
-
-    plugins : [
-            'karma-chrome-launcher',
-            'karma-firefox-launcher',
-            'karma-jasmine'
-            ],
-
-})}
diff --git a/Open-ILS/tests/staffweb/scripts/e2e-test.bat b/Open-ILS/tests/staffweb/scripts/e2e-test.bat
deleted file mode 100644 (file)
index 0b2aee6..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-@echo off
-
-REM Windows script for running e2e tests
-REM You have to run server and capture some browser first
-REM
-REM Requirements:
-REM - NodeJS (http://nodejs.org/)
-REM - Karma (npm install -g karma)
-
-set BASE_DIR=%~dp0
-karma start "%BASE_DIR%\..\config\karma-e2e.conf.js" %*
diff --git a/Open-ILS/tests/staffweb/scripts/e2e-test.sh b/Open-ILS/tests/staffweb/scripts/e2e-test.sh
deleted file mode 100755 (executable)
index 47fe827..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-BASE_DIR=`dirname $0`
-
-echo ""
-echo "Starting Karma Server (http://karma-runner.github.io)"
-echo $BASE_DIR
-echo "-------------------------------------------------------------------"
-
-$BASE_DIR/../node_modules/karma/bin/karma start $BASE_DIR/../config/karma-e2e.conf.js $*
diff --git a/Open-ILS/tests/staffweb/scripts/test.bat b/Open-ILS/tests/staffweb/scripts/test.bat
deleted file mode 100644 (file)
index 6571f8f..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-@echo off\r
-\r
-REM Windows script for running unit tests\r
-REM You have to run server and capture some browser first\r
-REM\r
-REM Requirements:\r
-REM - NodeJS (http://nodejs.org/)\r
-REM - Karma (npm install -g karma)\r
-\r
-set BASE_DIR=%~dp0\r
-karma start "%BASE_DIR%\..\config\karma.conf.js" %*\r
diff --git a/Open-ILS/tests/staffweb/scripts/test.sh b/Open-ILS/tests/staffweb/scripts/test.sh
deleted file mode 100755 (executable)
index f6db762..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-BASE_DIR=`dirname $0`
-
-echo ""
-echo "Starting Karma Server (http://karma-runner.github.io)"
-echo "-------------------------------------------------------------------"
-
-#$BASE_DIR/../node_modules/karma/bin/karma start $BASE_DIR/../config/karma.conf.js $*
-karma start $BASE_DIR/../config/karma.conf.js $*
diff --git a/Open-ILS/tests/staffweb/scripts/watchr.rb b/Open-ILS/tests/staffweb/scripts/watchr.rb
deleted file mode 100755 (executable)
index 89ef656..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env watchr
-
-# config file for watchr http://github.com/mynyml/watchr
-# install: gem install watchr
-# run: watch watchr.rb
-# note: make sure that you have jstd server running (server.sh) and a browser captured
-
-log_file = File.expand_path(File.dirname(__FILE__) + '/../logs/jstd.log')
-
-`cd ..`
-`touch #{log_file}`
-
-puts "String watchr... log file: #{log_file}"
-
-watch( '(app/js|test/unit)' )  do
-  `echo "\n\ntest run started @ \`date\`" > #{log_file}`
-  `scripts/test.sh &> #{log_file}`
-end
-
diff --git a/Open-ILS/tests/staffweb/scripts/web-server.js b/Open-ILS/tests/staffweb/scripts/web-server.js
deleted file mode 100755 (executable)
index 3f74441..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-#!/usr/bin/env node
-
-var util = require('util'),
-    http = require('http'),
-    fs = require('fs'),
-    url = require('url'),
-    events = require('events');
-
-var DEFAULT_PORT = 8000;
-
-function main(argv) {
-  new HttpServer({
-    'GET': createServlet(StaticServlet),
-    'HEAD': createServlet(StaticServlet)
-  }).start(Number(argv[2]) || DEFAULT_PORT);
-}
-
-function escapeHtml(value) {
-  return value.toString().
-    replace('<', '&lt;').
-    replace('>', '&gt;').
-    replace('"', '&quot;');
-}
-
-function createServlet(Class) {
-  var servlet = new Class();
-  return servlet.handleRequest.bind(servlet);
-}
-
-/**
- * An Http server implementation that uses a map of methods to decide
- * action routing.
- *
- * @param {Object} Map of method => Handler function
- */
-function HttpServer(handlers) {
-  this.handlers = handlers;
-  this.server = http.createServer(this.handleRequest_.bind(this));
-}
-
-HttpServer.prototype.start = function(port) {
-  this.port = port;
-  this.server.listen(port);
-  util.puts('Http Server running at http://localhost:' + port + '/');
-};
-
-HttpServer.prototype.parseUrl_ = function(urlString) {
-  var parsed = url.parse(urlString);
-  parsed.pathname = url.resolve('/', parsed.pathname);
-  return url.parse(url.format(parsed), true);
-};
-
-HttpServer.prototype.handleRequest_ = function(req, res) {
-  var logEntry = req.method + ' ' + req.url;
-  if (req.headers['user-agent']) {
-    logEntry += ' ' + req.headers['user-agent'];
-  }
-  util.puts(logEntry);
-  req.url = this.parseUrl_(req.url);
-  var handler = this.handlers[req.method];
-  if (!handler) {
-    res.writeHead(501);
-    res.end();
-  } else {
-    handler.call(this, req, res);
-  }
-};
-
-/**
- * Handles static content.
- */
-function StaticServlet() {}
-
-StaticServlet.MimeMap = {
-  'txt': 'text/plain',
-  'html': 'text/html',
-  'css': 'text/css',
-  'xml': 'application/xml',
-  'json': 'application/json',
-  'js': 'application/javascript',
-  'jpg': 'image/jpeg',
-  'jpeg': 'image/jpeg',
-  'gif': 'image/gif',
-  'png': 'image/png',
-  'svg': 'image/svg+xml'
-};
-
-StaticServlet.prototype.handleRequest = function(req, res) {
-  var self = this;
-  var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
-    return String.fromCharCode(parseInt(hex, 16));
-  });
-  var parts = path.split('/');
-  if (parts[parts.length-1].charAt(0) === '.')
-    return self.sendForbidden_(req, res, path);
-  fs.stat(path, function(err, stat) {
-    if (err)
-      return self.sendMissing_(req, res, path);
-    if (stat.isDirectory())
-      return self.sendDirectory_(req, res, path);
-    return self.sendFile_(req, res, path);
-  });
-}
-
-StaticServlet.prototype.sendError_ = function(req, res, error) {
-  res.writeHead(500, {
-      'Content-Type': 'text/html'
-  });
-  res.write('<!doctype html>\n');
-  res.write('<title>Internal Server Error</title>\n');
-  res.write('<h1>Internal Server Error</h1>');
-  res.write('<pre>' + escapeHtml(util.inspect(error)) + '</pre>');
-  util.puts('500 Internal Server Error');
-  util.puts(util.inspect(error));
-};
-
-StaticServlet.prototype.sendMissing_ = function(req, res, path) {
-  path = path.substring(1);
-  res.writeHead(404, {
-      'Content-Type': 'text/html'
-  });
-  res.write('<!doctype html>\n');
-  res.write('<title>404 Not Found</title>\n');
-  res.write('<h1>Not Found</h1>');
-  res.write(
-    '<p>The requested URL ' +
-    escapeHtml(path) +
-    ' was not found on this server.</p>'
-  );
-  res.end();
-  util.puts('404 Not Found: ' + path);
-};
-
-StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
-  path = path.substring(1);
-  res.writeHead(403, {
-      'Content-Type': 'text/html'
-  });
-  res.write('<!doctype html>\n');
-  res.write('<title>403 Forbidden</title>\n');
-  res.write('<h1>Forbidden</h1>');
-  res.write(
-    '<p>You do not have permission to access ' +
-    escapeHtml(path) + ' on this server.</p>'
-  );
-  res.end();
-  util.puts('403 Forbidden: ' + path);
-};
-
-StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
-  res.writeHead(301, {
-      'Content-Type': 'text/html',
-      'Location': redirectUrl
-  });
-  res.write('<!doctype html>\n');
-  res.write('<title>301 Moved Permanently</title>\n');
-  res.write('<h1>Moved Permanently</h1>');
-  res.write(
-    '<p>The document has moved <a href="' +
-    redirectUrl +
-    '">here</a>.</p>'
-  );
-  res.end();
-  util.puts('301 Moved Permanently: ' + redirectUrl);
-};
-
-StaticServlet.prototype.sendFile_ = function(req, res, path) {
-  var self = this;
-  var file = fs.createReadStream(path);
-  res.writeHead(200, {
-    'Content-Type': StaticServlet.
-      MimeMap[path.split('.').pop()] || 'text/plain'
-  });
-  if (req.method === 'HEAD') {
-    res.end();
-  } else {
-    file.on('data', res.write.bind(res));
-    file.on('close', function() {
-      res.end();
-    });
-    file.on('error', function(error) {
-      self.sendError_(req, res, error);
-    });
-  }
-};
-
-StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
-  var self = this;
-  if (path.match(/[^\/]$/)) {
-    req.url.pathname += '/';
-    var redirectUrl = url.format(url.parse(url.format(req.url)));
-    return self.sendRedirect_(req, res, redirectUrl);
-  }
-  fs.readdir(path, function(err, files) {
-    if (err)
-      return self.sendError_(req, res, error);
-
-    if (!files.length)
-      return self.writeDirectoryIndex_(req, res, path, []);
-
-    var remaining = files.length;
-    files.forEach(function(fileName, index) {
-      fs.stat(path + '/' + fileName, function(err, stat) {
-        if (err)
-          return self.sendError_(req, res, err);
-        if (stat.isDirectory()) {
-          files[index] = fileName + '/';
-        }
-        if (!(--remaining))
-          return self.writeDirectoryIndex_(req, res, path, files);
-      });
-    });
-  });
-};
-
-StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
-  path = path.substring(1);
-  res.writeHead(200, {
-    'Content-Type': 'text/html'
-  });
-  if (req.method === 'HEAD') {
-    res.end();
-    return;
-  }
-  res.write('<!doctype html>\n');
-  res.write('<title>' + escapeHtml(path) + '</title>\n');
-  res.write('<style>\n');
-  res.write('  ol { list-style-type: none; font-size: 1.2em; }\n');
-  res.write('</style>\n');
-  res.write('<h1>Directory: ' + escapeHtml(path) + '</h1>');
-  res.write('<ol>');
-  files.forEach(function(fileName) {
-    if (fileName.charAt(0) !== '.') {
-      res.write('<li><a href="' +
-        escapeHtml(fileName) + '">' +
-        escapeHtml(fileName) + '</a></li>');
-    }
-  });
-  res.write('</ol>');
-  res.end();
-};
-
-// Must be last,
-main(process.argv);
diff --git a/Open-ILS/tests/staffweb/test/unit/egHomeApp.js b/Open-ILS/tests/staffweb/test/unit/egHomeApp.js
deleted file mode 100644 (file)
index 31483e6..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict';
-
-describe('egHomeControllers', function(){
-  beforeEach(module('egHome'));
-
-  /* ---- LoginCtrl ---------------------------------- */
-
-  var loginCtrl, loginScope;
-  beforeEach(inject(function ($rootScope, $controller, $location) {
-      // pass the workstation name via (mock) URL param
-      $location.search({ws : 'TestWorkstation'});
-
-      loginScope = $rootScope.$new();
-      loginCtrl = $controller('LoginCtrl', {$scope: loginScope});
-  }));
-
-  it('should focus the login controller', inject(function() {
-    expect(loginScope.focusMe).toBe(true);
-  }));
-
-  it('should set the login workstation', inject(function() {
-    expect(loginScope.args.workstation).toEqual('TestWorkstation');
-  }));
-
-});
diff --git a/Open-ILS/tests/staffweb/test/unit/egPatronApp.js b/Open-ILS/tests/staffweb/test/unit/egPatronApp.js
deleted file mode 100644 (file)
index df9d829..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-'use strict';
-
-describe('egPatronAppTest', function(){
-  beforeEach(module('egPatronApp'));
-
-  // basic controller sanity checks
-  
-  var patronCtrl, patronScope;
-  beforeEach(inject(function ($rootScope, $controller, $location) {
-      patronScope = $rootScope.$new();
-      patronCtrl = $controller('PatronCtrl', {$scope: patronScope});
-  }));
-
-  /** patronSvc tests **/
-  describe('patronSvcTests', function() {
-
-    it('patronSvc should start with empty lists', inject(function(patronSvc) {
-        expect(patronSvc.patrons.count()).toEqual(0);
-    }));
-
-    it('patronSvc reset should clear data', inject(function(patronSvc) {
-        patronSvc.checkout_overrides.a = 1;
-        expect(Object.keys(patronSvc.checkout_overrides).length).toBe(1);
-        patronSvc.resetPatronLists();
-        expect(Object.keys(patronSvc.checkout_overrides).length).toBe(0);
-        expect(patronSvc.holds.items.length).toBe(0);
-    }));
-
-  });
-
-});
diff --git a/Open-ILS/tests/staffweb/update-angular.sh b/Open-ILS/tests/staffweb/update-angular.sh
deleted file mode 100755 (executable)
index dd02589..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#! /bin/sh
-
-# --------------------------------------
-# modified added by berick / 2013-12-12
-#  * create needed dirs
-#  * udpate angular-ui-bootstrap code
-
-set -e;
-
-if [ -z "$1" ]; then
-    echo  "$0 <angular-version> <angular-bootstrap-version>"
-    exit;
-fi;
-
-mkdir -p tmp
-mkdir -p app/lib;
-mkdir -p test/lib/angular
-mkdir -p test/lib/angular-ui-bootstrap
-
-curl https://raw.github.com/angular/code.angularjs.org/master/$1/angular-$1.zip -o tmp/angular.zip
-rm -fr app/lib/angular
-unzip tmp/angular.zip -d app/lib
-mv app/lib/angular-$1 app/lib/angular
-rm -fr app/lib/angular/docs
-mv app/lib/angular/angular-mocks.js test/lib/angular
-mv app/lib/angular/angular-scenario.js test/lib/angular
-cp app/lib/angular/version.txt test/lib/angular
-
-if [ -n "$2" ]; then
-  mkdir -p app/lib/angular-ui-bootstrap/
-  curl http://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/$2/ui-bootstrap-tpls.min.js \
-    -o app/lib/angular-ui-bootstrap/angular-bootstrap-min.js
-fi;
-
diff --git a/Open-ILS/web/js/ui/default/staff/Gruntfile.js b/Open-ILS/web/js/ui/default/staff/Gruntfile.js
new file mode 100644 (file)
index 0000000..2065d95
--- /dev/null
@@ -0,0 +1,98 @@
+module.exports = function(grunt) {
+
+  // Project configuration.
+  var config = { 
+    pkg: grunt.file.readJSON('package.json'),
+
+    // copy the JS files we care about from node-fetched dependencies
+    // into our build directory
+    copy: {
+      main: {
+        files: [{ 
+          dest: 'build/', 
+          flatten: true,
+          filter: 'isFile',
+          expand : true,
+          src: [
+            'node_modules/angular/lib/angular.min.js',
+            'node_modules/angular-route/angular-route.min.js',
+            'node_modules/angular-bootstrap/ui-bootstrap.js',
+            'node_modules/angular-hotkeys/build/hotkeys.min.js'
+          ]
+        }]
+      }
+    },
+
+    // confiure minification
+    uglify: {
+      options: {
+        banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
+      },
+      build: {
+        src: [
+            // These are concatenated in order in the final build file.
+            // The order is important.
+            'build/angular.min.js',
+            'build/angular-route.min.js',
+            'build/ui-bootstrap.js',
+            'build/hotkeys.min.js',
+            // NOTE: OpenSRF must be installed
+            '/openils/lib/javascript/JSON_v1.js',
+            '/openils/lib/javascript/opensrf.js',
+            '/openils/lib/javascript/opensrf_ws.js',
+            'services/core.js',
+            'services/strings.js',
+            'services/idl.js',
+            'services/event.js',
+            'services/net.js',
+            'services/auth.js',
+            'services/pcrud.js',
+            'services/env.js',
+            'services/org.js',
+            'services/startup.js',
+            'services/hatch.js',
+            'services/coresvc.js',
+            'services/statusbar.js',
+            'services/ui.js',
+            'navbar.js'
+        ],
+        dest: 'build/<%= pkg.name %>.<%= pkg.version %>.min.js'
+      }
+    },
+
+    // bare concat operation; useful for testing concat w/o minification
+    // to more easily detect when concat order is incorrect
+    concat: {
+      options: {
+       separator: ';',
+      }
+    },
+
+    // unit tests configuration
+    karma : {
+      unit: {
+        configFile: 'test/karma.conf.js',
+        //background: true  // for now, visually babysit unit tests
+      }
+    }
+  };
+
+  // tell concat about our uglify build options (instead of repeating them)
+  config.concat.build = config.uglify.build;
+
+  // apply our configuration
+  grunt.initConfig(config);
+
+  // Load our modules
+  grunt.loadNpmTasks('grunt-contrib-uglify');
+  grunt.loadNpmTasks('grunt-contrib-concat');
+  grunt.loadNpmTasks('grunt-contrib-copy');
+  grunt.loadNpmTasks('grunt-karma');
+
+  // note: "grunt concat" is not requried 
+  grunt.registerTask('all', ['copy', 'karma:unit', 'uglify']);
+
+  // test only, no minification
+  grunt.registerTask('test', ['copy', 'karma:unit']);
+
+};
diff --git a/Open-ILS/web/js/ui/default/staff/package.json b/Open-ILS/web/js/ui/default/staff/package.json
new file mode 100644 (file)
index 0000000..4974e46
--- /dev/null
@@ -0,0 +1,32 @@
+{
+  "name": "evergreen-staff-client",
+  "description": "Evergreen ILS Browser Staff Client",
+  "version": "0.0.1",
+  "license": "GPL",
+  "homepage": "http://evergreen-ils.org/",
+  "devDependencies": {
+    "angular": "^1.2.16",
+    "angular-bootstrap": "0.0.4",
+    "angular-hotkeys": "^1.2.0",
+    "grunt": "~0.4.4",
+    "grunt-cli": "^0.1.13",
+    "grunt-contrib-concat": "^0.4.0",
+    "grunt-contrib-copy": "^0.5.0",
+    "grunt-contrib-jasmine": "^0.6.4",
+    "grunt-contrib-uglify": "^0.4.0",
+    "grunt-karma": "^0.8.3",
+    "karma": "^0.12.14",
+    "karma-jasmine": "^0.1.5",
+    "karma-phantomjs-launcher": "^0.1.4",
+    "karma-script-launcher": "~0.1.0",
+    "karma-story-reporter": "^0.2.2"
+  },
+  "napa": {
+    "angular-route": "angular/bower-angular-route",
+    "angular-mocks": "angular/bower-angular-mocks"
+  },
+  "scripts": {
+    "install": "napa",
+    "test": "grunt test"
+  }
+}
diff --git a/Open-ILS/web/js/ui/default/staff/test/karma.conf.js b/Open-ILS/web/js/ui/default/staff/test/karma.conf.js
new file mode 100644 (file)
index 0000000..6acd77d
--- /dev/null
@@ -0,0 +1,79 @@
+module.exports = function(config){
+    config.set({
+    basePath : '../',
+
+    // config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
+    logLevel: config.LOG_DEBUG,
+
+    files : [
+      'build/angular.min.js',
+      'build/angular-route.min.js',
+      'node_modules/angular-mocks/angular-mocks.js', // testing only
+      'build/ui-bootstrap.js',
+      'build/hotkeys.min.js',
+      /* OpenSRF must be installed first */
+      '/openils/lib/javascript/md5.js',
+      '/openils/lib/javascript/JSON_v1.js',
+      '/openils/lib/javascript/opensrf.js',
+      '/openils/lib/javascript/opensrf_ws.js',
+      // service/*.js have to be loaded in order
+      'services/core.js',
+      'services/idl.js',
+      'services/strings.js',
+      'services/event.js',
+      'services/net.js',
+      'services/auth.js',
+      'services/pcrud.js',
+      'services/env.js',
+      'services/org.js',
+      'services/hatch.js',
+      'services/coresvc.js',
+      'services/user.js',
+      'services/startup.js',
+      'services/ui.js',
+      'services/statusbar.js',
+      'services/grid.js',
+      // load app scripts
+      'app.js',
+      'navbar.js',
+      'circ/**/*.js',
+      'cat/**/*.js',
+      'admin/**/*.js',
+      'test/unit/**/*.js'
+    ],
+
+    // test results reporter to use
+    // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
+    reporters: ['progress', 'coverage'],
+
+    // enable / disable colors in the output (reporters and logs)
+    colors: true,
+
+    // enable / disable watching file and executing tests whenever any file changes
+    autoWatch : false,
+
+    frameworks: ['jasmine'],
+
+    browsers: ['PhantomJS'],
+
+    // web server port
+    port: 9876,
+
+    /*
+    coverageReporter: {
+      type : 'html',
+      dir : 'coverage/',
+    },
+
+    preprocessors: {
+      '../src/*.js': ['coverage']
+    },
+    */
+
+    // If browser does not capture in given timeout [ms], kill it
+    captureTimeout: 60000,
+
+    // Continuous Integration mode
+    // if true, it capture browsers, run tests and exit
+    singleRun: true
+})}
diff --git a/Open-ILS/web/js/ui/default/staff/test/unit/egHomeApp.js b/Open-ILS/web/js/ui/default/staff/test/unit/egHomeApp.js
new file mode 100644 (file)
index 0000000..eac4669
--- /dev/null
@@ -0,0 +1,21 @@
+'use strict';
+
+describe('egHomeControllers', function(){
+  beforeEach(module('egHome'));
+
+  /* ---- LoginCtrl ---------------------------------- */
+
+  var loginCtrl, loginScope;
+  beforeEach(inject(function ($rootScope, $controller, $location) {
+      // pass the workstation name via (mock) URL param
+      $location.search({ws : 'TestWorkstation'});
+
+      loginScope = $rootScope.$new();
+      loginCtrl = $controller('LoginCtrl', {$scope: loginScope});
+  }));
+
+  it('should focus the login controller', inject(function() {
+    expect(loginScope.focusMe).toBe(true);
+  }));
+
+});
diff --git a/Open-ILS/web/js/ui/default/staff/test/unit/egPatronApp.js b/Open-ILS/web/js/ui/default/staff/test/unit/egPatronApp.js
new file mode 100644 (file)
index 0000000..60e9b15
--- /dev/null
@@ -0,0 +1,31 @@
+'use strict';
+
+describe('egPatronAppTest', function(){
+  beforeEach(module('egPatronApp'));
+
+  // basic controller sanity checks
+  
+  var patronCtrl, patronScope;
+  beforeEach(inject(function ($rootScope, $controller, $location) {
+      patronScope = $rootScope.$new();
+      patronCtrl = $controller('PatronCtrl', {$scope: patronScope});
+  }));
+
+  /** patronSvc tests **/
+  describe('patronSvcTests', function() {
+
+    it('patronSvc should start with empty lists', inject(function(patronSvc) {
+        expect(patronSvc.patrons.length).toEqual(0);
+    }));
+
+    it('patronSvc reset should clear data', inject(function(patronSvc) {
+        patronSvc.checkout_overrides.a = 1;
+        expect(Object.keys(patronSvc.checkout_overrides).length).toBe(1);
+        patronSvc.resetPatronLists();
+        expect(Object.keys(patronSvc.checkout_overrides).length).toBe(0);
+        expect(patronSvc.holds.length).toBe(0);
+    }));
+
+  });
+
+});