Commit 1419673e by Dennis Jen

Added first model and view for the lens navigation, reworked tests to run jasmine via karma

  * added spec runner page for viewing and running test results
  * added karma configuration to run tests via phantomjs
  * added lens navigation view, which displays a basic message
  * data is inserted to the page template and passed to JS
  * added course model

AN-1293 # resolve
AN-1357 # resolve

Change-Id: I0cf7a3fb9f7b82e0ff167c8bb8cd7ad7f39e4d8a
parent 4681d81b
{% extends "base.html" %} {% extends "base.html" %}
{% load staticfiles %}
{% comment %} {% block view-name %}view-placeholder{% endblock view-name %}
Individual course centric overview view
{% endcomment %}
{% block view-name %}view-course-overview view-dashboard{% endblock view-name %} {% block content %}
{% block title %}{{ course_number }} {{ course_title }}| edX Analytics{% endblock title %} <div id="analytics-init-data" class="container" data-analytics-init='{{ page_data | safe }}'>
<div class="row">
<div class="col-xs-12 col-md-8">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras at nisi vestibulum, interdum elit in, pellentesque velit. Curabitur lacinia sollicitudin commodo. Nam enim velit, semper et lacinia at, imperdiet sit amet arcu. Praesent dictum porta tortor, sed congue massa euismod id. Mauris dignissim turpis eu risus volutpat, vel imperdiet nunc aliquet. Morbi est ipsum, mollis eleifend condimentum quis, porttitor ut ligula. Aliquam diam risus, bibendum in justo eget, lobortis ullamcorper lorem. Vivamus elit turpis, fringilla nec magna in, condimentum lobortis nunc. Maecenas nec arcu consequat, auctor ipsum ac, malesuada orci. Sed bibendum nulla enim, quis scelerisque quam mattis vitae. In eleifend, sem eu pulvinar dictum, dui ante faucibus eros, placerat dictum orci nibh eget justo. Morbi quis adipiscing arcu. Vestibulum semper metus ac aliquam dapibus. Sed feugiat vulputate massa, ac ultricies arcu congue vel. Ut eget egestas erat. Donec molestie sem eu leo consequat, nec posuere mauris lobortis.</p>
</div>
{% block nav-skip %} <div class="col-xs-6 col-md-4">
<div class="sr-only"> <p>
<h2>Skip to Parts of this Page</h2> Nunc in eleifend dolor, vel ornare orci. Nullam venenatis iaculis urna, vel feugiat est rutrum at. Nullam lacinia elementum nibh at condimentum. In adipiscing vel neque a mattis. Aliquam lobortis at odio et eleifend. Etiam suscipit vitae ante nec aliquet. Donec egestas velit at commodo sagittis. Ut imperdiet, turpis mattis rutrum accumsan, libero justo euismod tellus, at pulvinar orci felis sed lectus. Nullam ut sapien nec velit faucibus gravida ac ac orci. Nam feugiat massa nec bibendum tincidunt. In lobortis feugiat leo, ut laoreet lorem rutrum sit amet.
<ul> </p>
<li><a href="#nav">Main Navigation</a></li> </div>
<li><a href="#content">Main Content</a></li>
<li><a href="#sidebar">Side Content + Navigation</a></li>
</ul>
</div>
{% endblock nav-skip %}
{% block content %} <div class="col-xs-6 col-md-4">
<div class="row"> <p>Aliquam lacinia, felis at cursus dictum, justo augue placerat erat, sed lacinia leo quam id mauris. Maecenas venenatis ligula facilisis, vulputate purus sed, condimentum odio. Maecenas nisi nulla, pellentesque ac mollis non, aliquam in sem. Integer euismod, ipsum id cursus posuere, risus libero tristique nulla, sit amet blandit leo diam in arcu. Sed a libero interdum, suscipit dui eget, tincidunt orci. Vestibulum cursus tellus eget enim tempus, in commodo tortor fermentum. Phasellus ut dapibus eros, non condimentum purus. Proin tincidunt sem sit amet felis accumsan, sed vehicula ligula consectetur. Cras in odio urna. Aliquam ut leo aliquet, sollicitudin sem eget, pellentesque risus. Mauris at rhoncus arcu, vitae imperdiet dolor. In hac habitasse platea dictumst. Curabitur id magna leo. Maecenas aliquet vel augue et aliquam. In vitae est quis quam suscipit tempus eget sit amet lectus. Curabitur a porta lacus.</p>
<aside id="sidebar" class="col-sm-3 col-md-2 sidebar"> </div>
<div class="sidebar-group">
<!-- TODO: abstract this out into one view with .active logic -->
<ul class="nav nav-stacked">
<li class="active">
<a href="#">
<span class="link-label"><i class="ico fa fa-tachometer"></i> Overview</span>
<i class="link-ico fa fa-angle-right hidden-xs"></i>
</a>
</li>
<li>
<a href="#">
<span class="link-label"><i class="ico fa fa-child"></i> Enrollment</span>
<i class="link-ico fa fa-angle-right hidden-xs"></i>
</a>
</li>
<li>
<a href="#">
<span class="link-label"><i class="ico fa fa-tasks"></i> Engagement</span>
<i class="link-ico fa fa-angle-right hidden-xs"></i>
</a>
</li>
<li>
<a href="#">
<span class="link-label"><i class="ico fa fa-bar-chart-o"></i> Performance</span>
<i class="link-ico fa fa-angle-right hidden-xs"></i>
</a>
</li>
<li>
<a href="#">
<span class="link-label"><i class="ico fa fa-life-ring"></i> Help &amp; Support</span>
<i class="link-ico fa fa-angle-right hidden-xs"></i>
</a>
</li>
<li>
<a href="#">
<span class="link-label"><i class="ico fa fa-comments-o"></i> Contact</span>
<i class="link-ico fa fa-angle-right hidden-xs"></i>
</a>
</li>
</ul>
</div>
</aside>
<!-- / col -->
<main class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" id="content"> <div class="col-xs-6 col-md-4">
<div class="view-head"> <p>Duis quis sodales ipsum, laoreet auctor ipsum. Pellentesque consequat volutpat felis ac dictum. Maecenas sodales malesuada magna, ac laoreet urna consequat ut. Nunc eget ligula eleifend, ornare diam non, vulputate nunc. In sit amet commodo mauris. Vivamus pharetra arcu gravida tincidunt interdum. Suspendisse potenti. Fusce non tellus a metus interdum bibendum. Quisque id molestie ante, vel pellentesque magna. Sed gravida eleifend erat pharetra consectetur. Suspendisse cursus justo et faucibus porttitor. Nam feugiat purus in neque sodales, sed volutpat nunc hendrerit. Etiam venenatis pretium lectus in lacinia. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam viverra turpis eu ligula condimentum placerat.</p>
<h1>Overview</h1> </div>
</div>
<div class="view-intro"> <div class="col-xs-6 col-md-4">
<p class="lead text-muted"> <p>Praesent sed augue et urna bibendum fringilla. Nam nisi risus, ornare in pellentesque non, volutpat eu lacus. Etiam et ultricies magna, non facilisis augue. Aliquam eleifend eget sem in pretium. Praesent enim purus, posuere at orci ac, bibendum laoreet diam. Phasellus pharetra ullamcorper aliquet. Phasellus pellentesque, purus sit amet sollicitudin semper, turpis risus viverra dui, ut consectetur dolor neque in lectus. Etiam at mi turpis.</p>
[Intro summary area for Overview if needed] Curabitur blandit tempus porttitor. Etiam porta sem malesuada magna mollis euismod. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. </div>
</p> </div>
</div>
</main>
<!-- / col -->
</div> </div>
<!-- / row -->
{% endblock content %} {% endblock content %}
{% block javascript %}
<script data-main="{% static 'js/course-analytics.js' %}" src="{% static 'vendor/require.js' %}"></script>
{% endblock javascript %}
\ No newline at end of file
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
import json
from models import Course from models import Course
def analytics(request, course_id): def analytics(request, course_id):
page_data = {
'name': 'Dennis',
'courseId': str(course_id),
}
# we would ideally get this from the DB, but for now lets stub some data # we would ideally get this from the DB, but for now lets stub some data
context = {'user_name': 'Ed Xavier', context = {'user_name': 'Ed Xavier',
'course_number': 'MITx 7.3423', 'course_number': 'MITx 7.3423',
'course_title': 'Introduction to Awesomeness' 'course_title': 'Introduction to Awesomeness',
'page_data': json.dumps(page_data)
} }
return render(request, 'courses/analytics.html', context) return render(request, 'courses/analytics.html', context)
/**
* Configuration for requires.js.
*/
require.config({
baseUrl: '/static/',
waitSeconds: 60,
paths: {
jquery: 'vendor/jquery-1.11.1.min',
underscore: 'vendor/underscore-min',
backbone: 'vendor/backbone-min',
bootstrap: 'vendor/bootstrap/javascripts/bootstrap',
models: 'js/models',
views: 'js/views'
},
shim: {
bootstrap: {
deps: ['jquery'],
// http://stackoverflow.com/questions/13377373/shim-twitter-bootstrap-for-requirejs
// for making sure that bootstrap is loaded correctly
exports: '$.fn.popover'
},
underscore: {
exports: '_'
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone',
init: function (_, $) { Backbone.$ = $; return Backbone; }
} },
// load jquery and gettext automatically
deps: ['jquery']
});
require(['jquery', 'underscore', 'backbone',
'models/course-model', 'views/lens-navigation-view', 'bootstrap'],
function ($, _, Backbone, CourseModel, LensNavigationView) {
'use strict';
$(document).ready(function () {
// ok, we've loaded all the libraries and the page is loaded, so
// lets kick off our application
var application = {
onLoad: function() {
var model,
view,
jsonData = JSON.parse( $('#analytics-init-data')
.attr('data-analytics-init') );
// this data will be set by something else eventually
model = new CourseModel();
// lets assume that django is passing us the data in the correct format for now
model.set(jsonData);
view = new LensNavigationView({model: model});
view.render();
}
};
application.onLoad();
});
}
);
define(['backbone', 'jquery'], function(Backbone, $) {
'use strict';
var CourseModel = Backbone.Model.extend({
/**
* This doesn't do much currently. I want to test out getting this model working
* with requireJS, gulp, and jasmine.
*
* @returns {*}
*/
isEmpty: function() {
var self = this;
return !self.has('courseId');
}
});
return CourseModel;
});
\ No newline at end of file
(function () {
'use strict';
// this is a test that will always pass just so that we have something for
// jasmine to test with initially
describe('A suite', function () {
it('contains spec with an expectation', function () {
expect(true).toBe(true);
});
});
}());
\ No newline at end of file
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>edX Analaytics JS Tests</title>
<link rel="stylesheet" type="text/css" href="/static/vendor/jasmine/lib/jasmine-2.0.0/jasmine.css">
<script src="/static/vendor/require.js" data-main="/static/js/test/spec-runner"></script>
</head>
<body>
</body>
</html>
/**
* This is where your tests go. It should happen automatically when you add files
* to the karma configuration.
*/
var isBrowser = window.__karma__ === undefined;
var specs = [];
var config = {
// this is default location for the browser
baseUrl: '/static/',
paths: {
jquery: 'vendor/jquery-1.11.1.min',
bootstrap: 'vendor/bootstrap/javascripts/bootstrap',
underscore: 'vendor/underscore-min',
backbone: 'vendor/backbone-min',
models: 'js/models',
views: 'js/views',
jasmine: 'vendor/jasmine/lib/jasmine-2.0.0/jasmine',
'jasmine-html': 'vendor/jasmine/lib/jasmine-2.0.0/jasmine-html',
boot: 'vendor/jasmine/lib/jasmine-2.0.0/boot'
},
shim: {
bootstrap: {
deps: ['jquery']
},
underscore: {
exports: '_'
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
jasmine: {
exports: 'jasmine'
},
'jasmine-html': {
deps: ['jasmine'],
exports: 'jasmine'
},
boot: {
deps: ['jasmine', 'jasmine-html'],
exports: 'window.jasmineRequire'
}
}
};
// there are two paths -- one for running this in browser and one for running
// via gulp
if(isBrowser) {
// unfortunately, we can't read directories in the browser, so we need to list them
// here -- sorry!
specs = [
config.baseUrl + 'js/spec/specs/course-model-spec.js'
];
} else {
// you can automatically get the test files using karma's configs
for (var file in window.__karma__.files) {
if (/spec\.js$/.test(file)) {
specs.push(file);
}
}
// this is where karma puts the files
config.baseUrl = '/base/analytics_dashboard/static/';
// karam lets you list the test files here
config.deps = specs;
config.callback = window.__karma__.start;
}
require.config(config);
// the browser needs to kick off jasmine. The gulp task does it through
// node
if(isBrowser) {
//jasmine 2.0 needs boot.js to run, which loads on a window load, so this is a hack
// -- http://stackoverflow.com/questions/19240302/does-jasmine-2-0-really-not-work-with-require-js
require(['boot'], function () {
'use strict';
require(specs,
function () {
window.onload();
});
});
}
define(['models/course-model'], function(CourseModel) {
'use strict';
describe('Course model', function () {
it('should be empty', function () {
var model = new CourseModel();
expect(model.isEmpty()).toBe(true);
});
});
describe('Course model', function () {
it('should have an ID', function () {
var model = new CourseModel({courseId: 'test'});
expect(model.isEmpty()).toBe(false);
expect(model.get('courseId')).toBe('test');
});
});
});
define(['jquery', 'backbone', 'models/course-model'],
function($, Backbone, CourseModel){
'use strict';
var LensNavigationView = Backbone.View.extend({
el: '#lens-navigation',
// Renders the view's template to the UI
render: function() {
var self = this;
self.$el.append('<h2>Navigation Section for ' +
self.model.get('courseId') + '</h2>');
// Maintains chainability
return this;
}
});
return LensNavigationView;
}
);
\ No newline at end of file
Copyright (c) 2008-2011 Pivotal Labs
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.
/**
Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
[jasmine-gem]: http://github.com/pivotal/jasmine-gem
*/
(function() {
/**
* ## Require &amp; Instantiate
*
* Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
*/
window.jasmine = jasmineRequire.core(jasmineRequire);
/**
* Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
*/
jasmineRequire.html(jasmine);
/**
* Create the Jasmine environment. This is used to run all specs in a project.
*/
var env = jasmine.getEnv();
/**
* ## The Global Interface
*
* Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
*/
var jasmineInterface = {
describe: function(description, specDefinitions) {
return env.describe(description, specDefinitions);
},
xdescribe: function(description, specDefinitions) {
return env.xdescribe(description, specDefinitions);
},
it: function(desc, func) {
return env.it(desc, func);
},
xit: function(desc, func) {
return env.xit(desc, func);
},
beforeEach: function(beforeEachFunction) {
return env.beforeEach(beforeEachFunction);
},
afterEach: function(afterEachFunction) {
return env.afterEach(afterEachFunction);
},
expect: function(actual) {
return env.expect(actual);
},
pending: function() {
return env.pending();
},
spyOn: function(obj, methodName) {
return env.spyOn(obj, methodName);
},
jsApiReporter: new jasmine.JsApiReporter({
timer: new jasmine.Timer()
})
};
/**
* Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
*/
if (typeof window == "undefined" && typeof exports == "object") {
extend(exports, jasmineInterface);
} else {
extend(window, jasmineInterface);
}
/**
* Expose the interface for adding custom equality testers.
*/
jasmine.addCustomEqualityTester = function(tester) {
env.addCustomEqualityTester(tester);
};
/**
* Expose the interface for adding custom expectation matchers
*/
jasmine.addMatchers = function(matchers) {
return env.addMatchers(matchers);
};
/**
* Expose the mock interface for the JavaScript timeout functions
*/
jasmine.clock = function() {
return env.clock;
};
/**
* ## Runner Parameters
*
* More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
*/
var queryString = new jasmine.QueryString({
getWindowLocation: function() { return window.location; }
});
var catchingExceptions = queryString.getParam("catch");
env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
/**
* ## Reporters
* The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
*/
var htmlReporter = new jasmine.HtmlReporter({
env: env,
onRaiseExceptionsClick: function() { queryString.setParam("catch", !env.catchingExceptions()); },
getContainer: function() { return document.body; },
createElement: function() { return document.createElement.apply(document, arguments); },
createTextNode: function() { return document.createTextNode.apply(document, arguments); },
timer: new jasmine.Timer()
});
/**
* The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
*/
env.addReporter(jasmineInterface.jsApiReporter);
env.addReporter(htmlReporter);
/**
* Filter which specs will be run by matching the start of the full name against the `spec` query param.
*/
var specFilter = new jasmine.HtmlSpecFilter({
filterString: function() { return queryString.getParam("spec"); }
});
env.specFilter = function(spec) {
return specFilter.matches(spec.getFullName());
};
/**
* Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
*/
window.setTimeout = window.setTimeout;
window.setInterval = window.setInterval;
window.clearTimeout = window.clearTimeout;
window.clearInterval = window.clearInterval;
/**
* ## Execution
*
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
*/
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
htmlReporter.initialize();
env.execute();
};
/**
* Helper function for readability above.
*/
function extend(destination, source) {
for (var property in source) destination[property] = source[property];
return destination;
}
}());
/*
Copyright (c) 2008-2013 Pivotal Labs
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.
*/
function getJasmineRequireObj() {
if (typeof module !== "undefined" && module.exports) {
return exports;
} else {
window.jasmineRequire = window.jasmineRequire || {};
return window.jasmineRequire;
}
}
getJasmineRequireObj().console = function(jRequire, j$) {
j$.ConsoleReporter = jRequire.ConsoleReporter();
};
getJasmineRequireObj().ConsoleReporter = function() {
var noopTimer = {
start: function(){},
elapsed: function(){ return 0; }
};
function ConsoleReporter(options) {
var print = options.print,
showColors = options.showColors || false,
onComplete = options.onComplete || function() {},
timer = options.timer || noopTimer,
specCount,
failureCount,
failedSpecs = [],
pendingCount,
ansi = {
green: '\x1B[32m',
red: '\x1B[31m',
yellow: '\x1B[33m',
none: '\x1B[0m'
};
this.jasmineStarted = function() {
specCount = 0;
failureCount = 0;
pendingCount = 0;
print("Started");
printNewline();
timer.start();
};
this.jasmineDone = function() {
printNewline();
for (var i = 0; i < failedSpecs.length; i++) {
specFailureDetails(failedSpecs[i]);
}
printNewline();
var specCounts = specCount + " " + plural("spec", specCount) + ", " +
failureCount + " " + plural("failure", failureCount);
if (pendingCount) {
specCounts += ", " + pendingCount + " pending " + plural("spec", pendingCount);
}
print(specCounts);
printNewline();
var seconds = timer.elapsed() / 1000;
print("Finished in " + seconds + " " + plural("second", seconds));
printNewline();
onComplete(failureCount === 0);
};
this.specDone = function(result) {
specCount++;
if (result.status == "pending") {
pendingCount++;
print(colored("yellow", "*"));
return;
}
if (result.status == "passed") {
print(colored("green", '.'));
return;
}
if (result.status == "failed") {
failureCount++;
failedSpecs.push(result);
print(colored("red", 'F'));
}
};
return this;
function printNewline() {
print("\n");
}
function colored(color, str) {
return showColors ? (ansi[color] + str + ansi.none) : str;
}
function plural(str, count) {
return count == 1 ? str : str + "s";
}
function repeat(thing, times) {
var arr = [];
for (var i = 0; i < times; i++) {
arr.push(thing);
}
return arr;
}
function indent(str, spaces) {
var lines = (str || '').split("\n");
var newArr = [];
for (var i = 0; i < lines.length; i++) {
newArr.push(repeat(" ", spaces).join("") + lines[i]);
}
return newArr.join("\n");
}
function specFailureDetails(result) {
printNewline();
print(result.fullName);
for (var i = 0; i < result.failedExpectations.length; i++) {
var failedExpectation = result.failedExpectations[i];
printNewline();
print(indent(failedExpectation.stack, 2));
}
printNewline();
}
}
return ConsoleReporter;
};
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
.html-reporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
.html-reporter a { text-decoration: none; }
.html-reporter a:hover { text-decoration: underline; }
.html-reporter p, .html-reporter h1, .html-reporter h2, .html-reporter h3, .html-reporter h4, .html-reporter h5, .html-reporter h6 { margin: 0; line-height: 14px; }
.html-reporter .banner, .html-reporter .symbol-summary, .html-reporter .summary, .html-reporter .result-message, .html-reporter .spec .description, .html-reporter .spec-detail .description, .html-reporter .alert .bar, .html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; }
.html-reporter .banner .version { margin-left: 14px; }
.html-reporter #jasmine_content { position: fixed; right: 100%; }
.html-reporter .version { color: #aaaaaa; }
.html-reporter .banner { margin-top: 14px; }
.html-reporter .duration { color: #aaaaaa; float: right; }
.html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; }
.html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; }
.html-reporter .symbol-summary li.passed { font-size: 14px; }
.html-reporter .symbol-summary li.passed:before { color: #5e7d00; content: "\02022"; }
.html-reporter .symbol-summary li.failed { line-height: 9px; }
.html-reporter .symbol-summary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
.html-reporter .symbol-summary li.disabled { font-size: 14px; }
.html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; }
.html-reporter .symbol-summary li.pending { line-height: 17px; }
.html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; }
.html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
.html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
.html-reporter .bar.failed { background-color: #b03911; }
.html-reporter .bar.passed { background-color: #a6b779; }
.html-reporter .bar.skipped { background-color: #bababa; }
.html-reporter .bar.menu { background-color: #fff; color: #aaaaaa; }
.html-reporter .bar.menu a { color: #333333; }
.html-reporter .bar a { color: white; }
.html-reporter.spec-list .bar.menu.failure-list, .html-reporter.spec-list .results .failures { display: none; }
.html-reporter.failure-list .bar.menu.spec-list, .html-reporter.failure-list .summary { display: none; }
.html-reporter .running-alert { background-color: #666666; }
.html-reporter .results { margin-top: 14px; }
.html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
.html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
.html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
.html-reporter.showDetails .summary { display: none; }
.html-reporter.showDetails #details { display: block; }
.html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
.html-reporter .summary { margin-top: 14px; }
.html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; }
.html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; }
.html-reporter .summary li.passed a { color: #5e7d00; }
.html-reporter .summary li.failed a { color: #b03911; }
.html-reporter .summary li.pending a { color: #ba9d37; }
.html-reporter .description + .suite { margin-top: 0; }
.html-reporter .suite { margin-top: 14px; }
.html-reporter .suite a { color: #333333; }
.html-reporter .failures .spec-detail { margin-bottom: 28px; }
.html-reporter .failures .spec-detail .description { background-color: #b03911; }
.html-reporter .failures .spec-detail .description a { color: white; }
.html-reporter .result-message { padding-top: 14px; color: #333333; white-space: pre; }
.html-reporter .result-message span.result { display: block; }
.html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
...@@ -29,46 +29,31 @@ ...@@ -29,46 +29,31 @@
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]--> <![endif]-->
</head> </head>
<body class="{% block view-name %}{% endblock view-name %}"> <body class="{% block view-name %}{% endblock view-name %}">
{% block nav-skip %}
<div class="sr-only">
<h2>Skip to Parts of this Page</h2>
<ul>
<li><a href="#nav">Main Navigation</a></li>
<li><a href="#content">Main Content</a></li>
</ul>
</div>
{% endblock nav-skip %}
{% block header %} {% block header %}
{% include "header.html" %} {% include "header.html" %}
{% endblock header %} {% endblock header %}
<div class="container-fluid"> <div id='lens-navigation'></div>
{% block content %} {% block content %}
{% endblock content %} {% endblock content %}
{% block footer %} {% block footer %}
{% include "footer.html" %} {% include "footer.html" %}
{% endblock footer %} {% endblock footer %}
</div>
<!-- / container -->
<!-- Placed at the end of the document so the pages load faster --> <!-- Placed at the end of the document so the pages load faster -->
{% comment %} {% comment %}
Make sure to add your JS files here. You'll probably want to use block.super to add to the comment includes rather Make sure to add your JS files here. You'll probably want to use block.super to add to the comment includes rather
than override them. than override them.
{% endcomment %} {% endcomment %}
{% block javascript %}
{% compress js %} {% compress js %}
<script type="text/javascript" src="{% static 'vendor/jquery-1.11.1.min.js' %}"></script> {% block javascript %}
<script type="text/javascript" src="{% static 'vendor/holder.js' %}"></script>
<script type="text/javascript" src="{% static 'vendor/bootstrap/javascripts/bootstrap.js' %}"></script>
<script type="text/javascript" src="{% static 'js/analytics_dashboard.js' %}"></script>
{% endcompress %}
{% endblock %} {% endblock %}
{% endcompress %}
</body> </body>
</html> </html>
\ No newline at end of file
...@@ -3,33 +3,49 @@ ...@@ -3,33 +3,49 @@
var jshint = require('gulp-jshint'); var jshint = require('gulp-jshint');
var gulp = require('gulp'); var gulp = require('gulp');
var jasmine = require('gulp-jasmine'); var karma = require('karma').server;
var path = require('path');
// lint this file in addition to js files. Please add directories to this // lint this file in addition to js files. Please add directories to this
// as needed // as needed
var lintSources = [ var paths = {
// add your test directories here
spec: ['analytics_dashboard/static/js/spec/specs/course-model-spec.js'],
//spec: ['analytics_dashboard/static/js/spec/spec-runner.js'],
lint: [
'gulpfile.js', 'gulpfile.js',
'analytics_dashboard/static/js/*.js', 'analytics_dashboard/static/js/**/.js',
'analytics_dashboard/static/js/spec/*.js']; 'analytics_dashboard/static/js/test/**/.js'],
// add your test directories here karamaConf: 'karma.conf.js'
var testSources = ['analytics_dashboard/static/js/spec/test.js']; };
// kicks up karma to the tests once
function runKarma(configFile, cb) {
karma.start({
configFile: path.resolve(configFile),
singleRun: true
}, cb);
}
gulp.task('lint', function() { gulp.task('lint', function() {
return gulp.src(lintSources) return gulp.src(paths.lint)
.pipe(jshint()) .pipe(jshint())
.pipe(jshint.reporter('default')); .pipe(jshint.reporter('default'));
}); });
gulp.task('test', function () { // this task runs the tests. It doesn't give you very detailed results,
gulp.src(testSources) // so you may need to run the jasmine test page directly:
.pipe(jasmine()); // http://127.0.0.1:8000/static/js/test/spec-runner.html
gulp.task('test', function(cb) {
runKarma(paths.karamaConf, cb);
}); });
// these are the default tasks when you run gulp // these are the default tasks when you run gulp
gulp.task('default', ['lint', 'test']); gulp.task('default', ['lint', 'test']);
// type 'gulp watch' to continuously run linting // type 'gulp watch' to continuously run linting and tests
gulp.task('watch', function() { gulp.task('watch', function() {
gulp.watch(lintSources, ['lint', 'test']); gulp.watch(paths.spec, ['lint', 'test']);
}); });
}()); }());
// Karma configuration
// Generated on Thu Jun 26 2014 17:49:39 GMT-0400 (EDT)
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine', 'requirejs'],
// list of files / patterns to load in the browser
files: [
{pattern: 'analytics_dashboard/static/vendor/**/*.js', included: false},
{pattern: 'analytics_dashboard/static/js/models/**/*.js', included: false},
{pattern: 'analytics_dashboard/static/js/test/specs/*.js', included: false},
'analytics_dashboard/static/js/test/spec-runner.js'
],
// list of files to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
// you can also add Chrome or other browsers too
browsers: ['PhantomJS'],
captureTimeout: 60000,
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
});
};
...@@ -7,7 +7,13 @@ ...@@ -7,7 +7,13 @@
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"gulp": "^3.8.1", "gulp": "^3.8.1",
"gulp-jasmine": "^0.2.0", "gulp-jshint": "^1.6.3",
"gulp-jshint": "^1.6.3" "gulp-karma": "0.0.4",
"karma": "^0.12.16",
"karma-chrome-launcher": "^0.1.4",
"karma-jasmine": "^0.1.5",
"karma-requirejs": "^0.2.2",
"phantomjs": "^1.9.7-10",
"requirejs": "^2.1.14"
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment