Fixed race condition with requirejs template

This commit is contained in:
Jarrod Overson 2012-11-01 22:03:09 -07:00
parent 9a71d179ea
commit d44d81448b
188 changed files with 32241 additions and 62 deletions

View File

@ -2,7 +2,8 @@
root = true
[*]
indent_style = tab
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true

2
.gitignore vendored
View File

@ -2,3 +2,5 @@ node_modules
npm-debug.log
tmp
.idea
tmp

View File

@ -1,3 +1,5 @@
language: node_js
node_js:
- 0.8
before_script:
- npm install grunt-cli

View File

@ -1,4 +1,8 @@
v0.1.0:
date: 2012-11-01
v0.1.1:
date: 2012-11-07
changes:
- Work in progress, not yet officially released.
- Fixed race condition in requirejs template
v0.1.0:
date: 2012-11-07
changes:
- Ported grunt-jasmine-runner and grunt-jasmine-task to grunt-contrib

View File

@ -13,12 +13,48 @@ module.exports = function(grunt) {
jshint: {
all: [
'Gruntfile.js',
'tasks/*.js'
'tasks/**/*.js'
],
options: {
jshintrc: '.jshintrc'
}
},
watch: {
files: ['test/fixtures/pivotal/**/*.js'],
tasks: 'jasmine:pivotal:build'
},
connect : {
port : 8000,
base : '.'
},
jasmine : {
options : {
specs : 'test/fixtures/pivotal/spec/*Spec.js',
helpers : 'test/fixtures/pivotal/spec/*Helper.js'
},
pivotal : {
src : 'test/fixtures/pivotal/src/**/*.js'
},
customTemplate : {
src : 'test/fixtures/pivotal/src/**/*.js',
options : {
template : 'test/fixtures/customTemplate/custom.tmpl'
}
},
requirejs : {
src : 'test/fixtures/requirejs/src/**/*.js',
options : {
specs : 'test/fixtures/requirejs/spec/*Spec.js',
helpers : 'test/fixtures/requirejs/spec/*Helper.js',
host : 'http://127.0.0.1:<%= connect.port %>/',
template : 'requirejs',
templateOptions : {
baseUrl : './test/fixtures/requirejs/src/'
// requirejs : 'vendor/require-2.1.1.js'
}
}
}
},
nodeunit: {
tasks: ['test/*_test.js']
}
@ -28,8 +64,12 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-nodeunit');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-internal');
grunt.registerTask('test', ['jasmine', 'nodeunit']);
grunt.registerTask('default', ['test', 'build-contrib']);
grunt.registerTask('watch-test', ['connect', 'watch']);
grunt.registerTask('test', ['jasmine:pivotal', 'jasmine:customTemplate', 'connect', 'jasmine:requirejs', 'nodeunit']);
grunt.registerTask('default', ['jshint', 'test', 'build-contrib']);
};

197
README.md
View File

@ -28,54 +28,201 @@ If the plugin has been installed correctly, running `grunt --help` at the comman
## The jasmine task
#### Overview
### Overview
grunt-contrib-jasmine automatically builds and maintains your spec runner and runs your tests headlessly through
phantomjs
Substantial credit goes to [Camille Reynders](http://creynders.be/) (@creynders) for the first decent implementation
of jasmine through grunt which served as motivation for all the future work.
#### Run specs locally or on an ad hoc server
Run your tests on your local filesystem or via a server task like [grunt-contrib-connect][].
#### AMD Support
Includes a SpecRunner template customized for use with AMD modules and requirejs.
#### Customize your SpecRunner with your own template
Supply your own underscore template to automatically build your SpecRunner custom to your use.
### Flags
Name: `build`
Specify this flag in order to rebuild the specrunner and not delete it. This is useful when troublshooting templates,
running in a browser, or as part of a watch chain for use in a web browser, e.g.
```js
watch: {
pivotal : {
files: ['test/fixtures/pivotal/**/*.js'],
tasks: 'jasmine:pivotal:build'
}
}
```
```js
grunt.registerTask('dev', ['connect', 'watch']);
```
[grunt-contrib-connect]: https://github.com/gruntjs/grunt-contrib-connect
### Options
#### option1
Type: `Integer`
Default: `8080`
#### src
Type: `String|Array`
descr
*Minimatch* - This defines your source files. These are the files that you are testing.
#### option2
Type: `String`
Default: `foo`
#### options.specs
Type: `String|Array`
descr
*Minimatch* - These are your Jasmine specs.
#### option3
Type: `String`
Default: `.`
#### options.vendor
Type: `String|Array`
descr
*Minimatch* - These are third party libraries, generally loaded before anything else happens in your tests. You'll likely add things
like jQuery and Backbone here.
#### option4
Type: `Boolean`
Default: `false`
#### options.helpers
Type: `String|Array`
descr
*Minimatch* - These are non-source, non-spec helper files. In the default runner these are loaded after `vendor` files
#### options5
Type: `Function`
Default:
#### options.outfile
Type: `String`
Default: `_SpecRunner.html`
This is the auto-generated specfile that phantomjs will use to run your tests. This is automatically deleted upon normal
runs
#### options.host
Type: `String`
Default: ''
This is the host you want phantomjs to connect against to run your tests.
e.g. if using an ad hoc server from within grunt
```js
function() {
host : 'http://127.0.0.1:8000/'
```
Or, using templates
```js
host : 'http://127.0.0.1:<%= connect.port %>/'
```
Not defining a host will mean your specs will be run from the local filesystem.
#### options.template
Type: `String`
Default: `default`
Options: `default`, `requirejs`, `yourcustomtemplate.tmpl`
Specify a custom template to use when generating your Spec Runner. Templates are parsed as underscore templates and provided
the expanded list of files needed to build a specrunner.
#### options.templateOptions
Type: `Object`
Default: `{}`
These options will be passed to your template as an 'options' hash so that you can provide settings to your template.
**'requirejs'** default templateOptions :
```js
requirejs : __dirname + '/../vendor/require-#.#.#.js',
baseUrl : ''
```
- requirejs : the location of the included requirejs.
- baseUrl : set in `require.config({})`, sets the baseUrl for your modules (usually the directory your 'src' files are located in.
#### Basic Use
Sample configuration to run Pivotal Labs' example Jasmine application.
```js
jasmine : {
pivotal : {
src : 'test/fixtures/pivotal/src/**/*.js'
options : {
specs : 'test/fixtures/pivotal/spec/*Spec.js',
helpers : 'test/fixtures/pivotal/spec/*Helper.js'
}
}
}
```
### Usage examples
#### Supplying a custom template
#### Basic Use
Supplying a custom template to the above example
```js
jasmine : {
customTemplate : {
src : 'test/fixtures/pivotal/src/**/*.js',
options : {
specs : 'test/fixtures/pivotal/spec/*Spec.js',
helpers : 'test/fixtures/pivotal/spec/*Helper.js'
template : 'test/fixtures/customTemplate/custom.tmpl'
}
}
}
```
#### Sample RequireJS usage
```js
jasmine : {
requirejs : {
src : 'test/fixtures/requirejs/src/**/*.js',
options : {
specs : 'test/fixtures/requirejs/spec/*Spec.js',
helpers : 'test/fixtures/requirejs/spec/*Helper.js',
host : 'http://127.0.0.1:<%= connect.port %>/',
template : 'requirejs',
templateOptions : {
baseUrl : './test/fixtures/requirejs/src/'
}
}
}
}
```
#### RequireJS note
If you end up using the requirejs template, it's worth looking at the [RequireJS template](blob/master/tasks/jasmine/templates/RequireJSRunner.tmpl) in order to
familiarize yourself with how it loads your files. The gist of it is:
```js
require([*YOUR SOURCE*], function() {
require([*YOUR SPECS*], function() {
require([*JASMINE FILES*], function() {
// at this point your tests are already running.
}
}
}
```
## Release History
* 2012-10-31 - v0.1.0 - Work in progress, not yet officially released.
* 2012-11-06 - v0.1.1 - Fixed race condition in requirejs template
* 2012-11-06 - v0.1.0 - Ported grunt-jasmine-runner and grunt-jasmine-task to grunt-contrib
--
Task submitted by <a href="http://jarrodoverson.com">Jarrod Overson</a>.
*Generated on Thu Nov 01 2012 22:01:17.*
*Generated on Wed Nov 07 2012 10:53:51.*

View File

@ -1,3 +1,68 @@
# Usage examples
## Basic Use
Sample configuration to run Pivotal Labs' example Jasmine application.
```js
jasmine : {
pivotal : {
src : 'test/fixtures/pivotal/src/**/*.js'
options : {
specs : 'test/fixtures/pivotal/spec/*Spec.js',
helpers : 'test/fixtures/pivotal/spec/*Helper.js'
}
}
}
```
## Supplying a custom template
Supplying a custom template to the above example
```js
jasmine : {
customTemplate : {
src : 'test/fixtures/pivotal/src/**/*.js',
options : {
specs : 'test/fixtures/pivotal/spec/*Spec.js',
helpers : 'test/fixtures/pivotal/spec/*Helper.js'
template : 'test/fixtures/customTemplate/custom.tmpl'
}
}
}
```
## Sample RequireJS usage
```js
jasmine : {
requirejs : {
src : 'test/fixtures/requirejs/src/**/*.js',
options : {
specs : 'test/fixtures/requirejs/spec/*Spec.js',
helpers : 'test/fixtures/requirejs/spec/*Helper.js',
host : 'http://127.0.0.1:<%= connect.port %>/',
template : 'requirejs',
templateOptions : {
baseUrl : './test/fixtures/requirejs/src/'
}
}
}
}
```
## RequireJS note
If you end up using the requirejs template, it's worth looking at the [RequireJS template](blob/master/tasks/jasmine/templates/RequireJSRunner.tmpl) in order to
familiarize yourself with how it loads your files. The gist of it is:
```js
require([*YOUR SOURCE*], function() {
require([*YOUR SPECS*], function() {
require([*JASMINE FILES*], function() {
// at this point your tests are already running.
}
}
}
```

View File

@ -1,34 +1,75 @@
# Options
## option1
Type: `Integer`
Default: `8080`
## src
Type: `String|Array`
descr
*Minimatch* - This defines your source files. These are the files that you are testing.
## option2
Type: `String`
Default: `foo`
## options.specs
Type: `String|Array`
descr
*Minimatch* - These are your Jasmine specs.
## option3
Type: `String`
Default: `.`
## options.vendor
Type: `String|Array`
descr
*Minimatch* - These are third party libraries, generally loaded before anything else happens in your tests. You'll likely add things
like jQuery and Backbone here.
## option4
Type: `Boolean`
Default: `false`
## options.helpers
Type: `String|Array`
descr
*Minimatch* - These are non-source, non-spec helper files. In the default runner these are loaded after `vendor` files
## options5
Type: `Function`
Default:
## options.outfile
Type: `String`
Default: `_SpecRunner.html`
This is the auto-generated specfile that phantomjs will use to run your tests. This is automatically deleted upon normal
runs
## options.host
Type: `String`
Default: ''
This is the host you want phantomjs to connect against to run your tests.
e.g. if using an ad hoc server from within grunt
```js
function() {
}
host : 'http://127.0.0.1:8000/'
```
Or, using templates
```js
host : 'http://127.0.0.1:<%= connect.port %>/'
```
Not defining a host will mean your specs will be run from the local filesystem.
## options.template
Type: `String`
Default: `default`
Options: `default`, `requirejs`, `yourcustomtemplate.tmpl`
Specify a custom template to use when generating your Spec Runner. Templates are parsed as underscore templates and provided
the expanded list of files needed to build a specrunner.
## options.templateOptions
Type: `Object`
Default: `{}`
These options will be passed to your template as an 'options' hash so that you can provide settings to your template.
**'requirejs'** default templateOptions :
```js
requirejs : __dirname + '/../vendor/require-#.#.#.js',
baseUrl : ''
```
- requirejs : the location of the included requirejs.
- baseUrl : set in `require.config({})`, sets the baseUrl for your modules (usually the directory your 'src' files are located in.

View File

@ -1,2 +1,42 @@
## Overview
# Overview
grunt-contrib-jasmine automatically builds and maintains your spec runner and runs your tests headlessly through
phantomjs
Substantial credit goes to [Camille Reynders](http://creynders.be/) (@creynders) for the first decent implementation
of jasmine through grunt which served as motivation for all the future work.
## Run specs locally or on an ad hoc server
Run your tests on your local filesystem or via a server task like [grunt-contrib-connect][].
## AMD Support
Includes a SpecRunner template customized for use with AMD modules and requirejs.
## Customize your SpecRunner with your own template
Supply your own underscore template to automatically build your SpecRunner custom to your use.
# Flags
Name: `build`
Specify this flag in order to rebuild the specrunner and not delete it. This is useful when troublshooting templates,
running in a browser, or as part of a watch chain for use in a web browser, e.g.
```js
watch: {
pivotal : {
files: ['test/fixtures/pivotal/**/*.js'],
tasks: 'jasmine:pivotal:build'
}
}
```
```js
grunt.registerTask('dev', ['connect', 'watch']);
```
[grunt-contrib-connect]: https://github.com/gruntjs/grunt-contrib-connect

View File

@ -25,9 +25,12 @@
"node": ">=0.8.0"
},
"scripts": {
"test": "grunt test"
"test": "./node_modules/grunt-cli/bin/grunt test -vd"
},
"dependencies": {
"grunt-lib-phantomjs": "~0.1.0",
"grunt-contrib-connect": "~0.1.0",
"grunt-contrib-watch": "~0.1.4"
},
"devDependencies": {
"grunt": "~0.4.0a",

View File

@ -5,24 +5,220 @@
* Licensed under the MIT license.
*/
/*jshint node:true, curly:false*/
'use strict';
module.exports = function(grunt) {
var path = require('path');
// node api
var fs = require('fs'),
path = require('path');
grunt.registerTask('jasmine', 'Run jasmine specs headlessly through PhantomJS.', function() {
// npm lib
var phantomjs = require('grunt-lib-phantomjs').init(grunt);
// local lib
var jasmine = require('./lib/jasmine');
var status = {
specs : 0,
failed : 0,
passed : 0,
total : 0,
skipped : 0,
duration : 0
};
var runners = {
default : __dirname + '/jasmine/templates/DefaultRunner.tmpl',
requirejs : __dirname + '/jasmine/templates/RequireJSRunner.tmpl'
};
var runnerOptions = {
requirejs : {
requirejs : jasmine.getRelativeFileList(__dirname + '/../vendor/require-2.1.1.js')[0]
}
};
grunt.registerMultiTask('jasmine', 'Run jasmine specs headlessly through PhantomJS.', function() {
// Merge task-specific options with these defaults.
var options = grunt.util._.defaults(grunt.config('jasmine'), {
var options = this.options({
timeout : 10000,
specs : [],
helpers : [],
vendor : [],
outfile : '_SpecRunner.html',
host : '',
template: 'default',
templateOptions : {},
phantomjs : {}
});
grunt.util._.defaults(options.templateOptions, runnerOptions[options.template] || {});
if (!options.template.match(/\.tmpl$/)) options.template = runners[options.template];
if (grunt.option('debug')) {
grunt.log.debug(options);
}
// Start server.
grunt.log.writeln('');
setup(options);
var files = grunt.file.expandFiles(grunt.util._.pluck(this.files, 'src'));
jasmine.buildSpecrunner(files,options);
// If we're just building (e.g. for web), skip phantom.
if (this.flags.build) return;
var done = this.async();
phantomRunner(options, function(err,status) {
if (err) grunt.log.error(err);
if (status.failed === 0) grunt.log.ok('0 failures');
else grunt.log.error(status.failed + ' failures');
teardown(options);
done(!err && status.failed === 0);
});
});
function phantomRunner(options,cb){
var file = options.outfile;
if (options.host) {
file = options.host + options.outfile;
}
grunt.verbose.subhead('Testing jasmine specs via phantom').or.writeln('Testing jasmine specs via phantom');
phantomjs.spawn(file, {
failCode : 90,
options : options,
done : function(err){
cb(err,status);
}
});
}
function teardown(options) {
if (fs.statSync(options.outfile).isFile()) fs.unlink(options.outfile);
// Have to explicitly unregister nested wildcards. Need to file a bug for EventEmitter2
phantomjs.removeAllListeners('*');
phantomjs.removeAllListeners('jasmine.*');
phantomjs.removeAllListeners('error.*');
phantomjs.removeAllListeners('jasmine.done.*');
}
function setup(options) {
var thisRun = {};
phantomjs.on('fail.timeout',function(){
grunt.log.writeln();
grunt.warn('PhantomJS timed out, possibly due to an unfinished async spec.', 90);
});
phantomjs.on('console',console.log.bind(console));
phantomjs.on('verbose',grunt.verbose.writeln.bind(grunt.verbose));
phantomjs.on('debug', grunt.log.debug.bind(grunt.log, 'phantomjs'));
phantomjs.on('write', grunt.log.write.bind(grunt.log));
phantomjs.on('writeln', grunt.log.writeln.bind(grunt.log));
phantomjs.on('error.onError',function(string, trace){
if (trace) {
grunt.log.error(string.red + ' at ');
trace.forEach(function(line) {
var file = line.file.replace(/^file:/,'');
var message = grunt.util._('%s:%d %s').sprintf(path.relative('.',file), line.line, line.function);
grunt.log.error(message.red);
});
} else {
grunt.log.error(string.red);
}
});
phantomjs.onAny(function() {
var args = [this.event].concat(grunt.util.toArray(arguments));
grunt.event.emit.apply(grunt.event, args);
});
phantomjs.on('jasmine.writeFile',function(type,filename, xml){
var dir = options[type] && options[type].output;
if (dir) {
grunt.file.mkdir(dir);
grunt.file.write(path.join(dir, filename), xml);
}
});
phantomjs.on('jasmine.reportRunnerStarting',function(suites) {
grunt.verbose.writeln('Starting...');
thisRun.start_time = (new Date()).getTime();
thisRun.executed_specs = 0;
thisRun.passed_specs = 0;
});
phantomjs.on('jasmine.reportSpecStarting',function(spec) {
thisRun.executed_specs++;
grunt.verbose.write(spec.suite.description + ':' + spec.description + '...');
});
phantomjs.on('jasmine.reportSuiteResults',function(suite){
//grunt.verbose.writeln(suite.description + ": " + suite.results.passedCount + " of " + suite.results.totalCount + " passed.");
});
phantomjs.on('jasmine.reportSpecResults',function(specId, result,summary) {
if (result.passed) thisRun.passed_specs++;
grunt.verbose.writeln(summary.passed ? result.msg.green : result.msg.red);
for (var i = 0; i < result.messages.length; i++) {
var item = result.messages[i];
if (item.type === 'log') {
grunt.verbose.writeln(item.toString());
} else if (item.type === 'expect' && !item.passed_) {
grunt.log.writeln(summary.description + ':' + result.msg.red);
phantomjs.emit('onError', item.message, item.trace);
}
}
phantomjs.emit( 'jasmine.testDone', result.totalCount, result.passedCount, result.failedCount, result.skipped );
});
phantomjs.on('jasmine.reportRunnerResults',function(){
grunt.verbose.writeln('Runner finished');
var dur = (new Date()).getTime() - thisRun.start_time;
var failed = thisRun.executed_specs - thisRun.passed_specs;
var spec_str = thisRun.executed_specs + (thisRun.executed_specs === 1 ? " spec, " : " specs, ");
var fail_str = failed + (failed === 1 ? " failure in " : " failures in ");
grunt.log.writeln(spec_str + fail_str + (dur/1000) + "s.");
});
phantomjs.on('jasmine.testDone',function(totalAssertions, passedAssertions, failedAssertions, skippedAssertions){
status.specs++;
status.failed += failedAssertions;
status.passed += passedAssertions;
status.total += totalAssertions;
status.skipped += skippedAssertions;
});
phantomjs.on('jasmine.done',function(elapsed){
phantomjs.halt();
status.duration = elapsed;
});
phantomjs.on('jasmine.done.PhantomReporter',function(){
phantomjs.emit('jasmine.done');
});
phantomjs.on('jasmine.done_fail',function(url){
grunt.log.error();
grunt.warn('PhantomJS unable to load "' + url + '" URI.', 90);
});
}
};

View File

@ -0,0 +1,31 @@
/*global jasmine:false, window:false, document:false*/
(function(){
'use strict';
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
if (document.readyState !== 'complete') {
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
jasmineEnv.execute();
};
} else {
jasmineEnv.execute();
}
}());

View File

@ -0,0 +1,187 @@
/* Based off of https://github.com/larrymyers/jasmine-reporters/ */
/*global phantom:false, jasmine:false*/
// Unused right now. Logic needs to be brought onto the grunt side, not on the phantom side
(function() {
"use strict";
function elapsed(startTime, endTime) {
return (endTime - startTime)/1000;
}
function ISODateString(d) {
function pad(n) { return n < 10 ? '0'+n : n; }
return d.getFullYear() + '-' +
pad(d.getMonth()+1) + '-' +
pad(d.getDate()) + 'T' +
pad(d.getHours()) + ':' +
pad(d.getMinutes()) + ':' +
pad(d.getSeconds());
}
function trim(str) {
return str.replace(/^\s+/, "" ).replace(/\s+$/, "" );
}
function escapeInvalidXmlChars(str) {
return str.replace(/\&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/\>/g, "&gt;")
.replace(/\"/g, "&quot;")
.replace(/\'/g, "&apos;");
}
/**
* Generates JUnit XML for the given spec run.
* Allows the test results to be used in java based CI
* systems like CruiseControl and Hudson.
*
* @param {string} savePath where to save the files
* @param {boolean} consolidate whether to save nested describes within the
* same file as their parent; default: true
* @param {boolean} useDotNotation whether to separate suite names with
* dots rather than spaces (ie "Class.init" not
* "Class init"); default: true
*/
function JUnitXmlReporter(savePath, consolidate, useDotNotation) {
this.savePath = savePath || '';
this.consolidate = typeof consolidate === 'undefined' ? true : consolidate;
this.useDotNotation = typeof useDotNotation === 'undefined' ? true : useDotNotation;
}
JUnitXmlReporter.finished_at = null; // will be updated after all files have been written
JUnitXmlReporter.prototype = {
reportSpecStarting: function(spec) {
spec.startTime = new Date();
if (!spec.suite.startTime) {
spec.suite.startTime = spec.startTime;
}
},
reportSpecResults: function(spec) {
var results = spec.results();
spec.didFail = !results.passed();
spec.duration = elapsed(spec.startTime, new Date());
spec.output = '<testcase classname="' + this.getFullName(spec.suite) +
'" name="' + escapeInvalidXmlChars(spec.description) + '" time="' + spec.duration + '">';
var failure = "";
var failures = 0;
var resultItems = results.getItems();
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type === 'expect' && result.passed && !result.passed()) {
failures += 1;
failure += (failures + ": " + escapeInvalidXmlChars(result.message) + " ");
}
}
if (failure) {
spec.output += "<failure>" + trim(failure) + "</failure>";
}
spec.output += "</testcase>";
},
reportSuiteResults: function(suite) {
var results = suite.results();
var specs = suite.specs();
var specOutput = "";
// for JUnit results, let's only include directly failed tests (not nested suites')
var failedCount = 0;
suite.status = results.passed() ? 'Passed.' : 'Failed.';
if (results.totalCount === 0) { // todo: change this to check results.skipped
suite.status = 'Skipped.';
}
// if a suite has no (active?) specs, reportSpecStarting is never called
// and thus the suite has no startTime -- account for that here
suite.startTime = suite.startTime || new Date();
suite.duration = elapsed(suite.startTime, new Date());
for (var i = 0; i < specs.length; i++) {
failedCount += specs[i].didFail ? 1 : 0;
specOutput += "\n " + specs[i].output;
}
suite.output = '\n<testsuite name="' + this.getFullName(suite) +
'" errors="0" tests="' + specs.length + '" failures="' + failedCount +
'" time="' + suite.duration + '" timestamp="' + new ISODateString(suite.startTime) + '">';
suite.output += specOutput;
suite.output += "\n</testsuite>";
},
reportRunnerResults: function(runner) {
var suites = runner.suites();
for (var i = 0; i < suites.length; i++) {
var suite = suites[i];
var fileName = 'TEST-' + this.getFullName(suite, true) + '.xml';
var output = '<?xml version="1.0" encoding="UTF-8" ?>';
// if we are consolidating, only write out top-level suites
if (this.consolidate && suite.parentSuite) {
continue;
}
else if (this.consolidate) {
output += "\n<testsuites>";
output += this.getNestedOutput(suite);
output += "\n</testsuites>";
this.writeFile(this.savePath + fileName, output);
}
else {
output += suite.output;
this.writeFile(this.savePath + fileName, output);
}
}
// When all done, make it known on JUnitXmlReporter
JUnitXmlReporter.finished_at = (new Date()).getTime();
phantom.sendMessage('jasmine.done.JUnitReporter');
},
getNestedOutput: function(suite) {
var output = suite.output;
for (var i = 0; i < suite.suites().length; i++) {
output += this.getNestedOutput(suite.suites()[i]);
}
return output;
},
writeFile: function(filename, text) {
phantom.sendMessage('jasmine.writeFile','junit',filename,text);
},
getFullName: function(suite, isFilename) {
var fullName;
if (this.useDotNotation) {
fullName = suite.description;
for (var parentSuite = suite.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
fullName = parentSuite.description + '.' + fullName;
}
}
else {
fullName = suite.getFullName();
}
// Either remove or escape invalid XML characters
if (isFilename) {
return fullName.replace(/[^\w]/g, "");
}
return escapeInvalidXmlChars(fullName);
},
log: function(str) {
var console = jasmine.getGlobal().console;
if (console && console.log) {
console.log(str);
}
}
};
jasmine.reporters = jasmine.reporters || {};
jasmine.reporters.JUnitXmlReporter = JUnitXmlReporter;
jasmine.getEnv().addReporter( new JUnitXmlReporter() );
}());

View File

@ -0,0 +1,193 @@
/*global window:false, alert:false, jasmine:false, Node:false, */
/*jshint curly:false*/
(function(){
'use strict';
var phantom = {};
phantom.sendMessage = function(){
var args = [].slice.call( arguments );
var payload = JSON.stringify( args );
if (window._phantom) {
alert( payload );
} else {
console.log(payload);
}
};
function PhantomReporter() {
this.started = false;
this.finished = false;
this.suites_ = [];
this.results_ = {};
this.buffer = '';
}
PhantomReporter.prototype.log = function(str) {
phantom.sendMessage('verbose',str);
};
PhantomReporter.prototype.reportRunnerStarting = function(runner) {
this.started = true;
var suites = runner.topLevelSuites();
for (var i = 0; i < suites.length; i++) {
var suite = suites[i];
this.suites_.push(this.summarize_(suite));
}
phantom.sendMessage('jasmine.reportRunnerStarting', this.suites_);
};
PhantomReporter.prototype.reportSpecStarting = function(spec) {
var message = {
suite : {
description : spec.suite.description
},
description : spec.description
};
phantom.sendMessage('jasmine.reportSpecStarting', message);
};
PhantomReporter.prototype.suites = function() {
return this.suites_;
};
PhantomReporter.prototype.summarize_ = function(suiteOrSpec) {
var isSuite = suiteOrSpec instanceof jasmine.Suite;
var summary = {
id: suiteOrSpec.id,
name: suiteOrSpec.description,
type: isSuite ? 'suite' : 'spec',
children: []
};
if (isSuite) {
var children = suiteOrSpec.children();
for (var i = 0; i < children.length; i++) {
summary.children.push(this.summarize_(children[i]));
}
}
return summary;
};
PhantomReporter.prototype.results = function() {
return this.results_;
};
PhantomReporter.prototype.resultsForSpec = function(specId) {
return this.results_[specId];
};
PhantomReporter.prototype.reportRunnerResults = function(runner) {
this.finished = true;
var specIds = runner.specs().map(function(a){return a.id;});
var summary = this.resultsForSpecs(specIds);
phantom.sendMessage('jasmine.reportRunnerResults',summary);
phantom.sendMessage('jasmine.done.PhantomReporter');
};
PhantomReporter.prototype.reportSuiteResults = function(suite) {
phantom.sendMessage('jasmine.reportSuiteResults',{
description : suite.description,
results : suite.results()
});
};
function stringify(obj) {
if (typeof obj !== 'object') return obj;
var cache = [], keyMap = [], tempArray, index;
var string = JSON.stringify(obj, function(key, value) {
// Let json stringify falsy values
if (!value) return value;
// If we're a node
if (value instanceof Node) return '[ Node ]';
// If we're a window (logic stolen from jQuery)
if (value.window && value.window === value.window.window) return '[ Window ]';
// Simple function reporting
if (typeof value === 'function') return '[ Function ]';
if (typeof value === 'object' && value !== null) {
// Check to see if we have a pseudo array that can be converted
if (value.length && (tempArray = Array.prototype.slice.call(value)).length === value.length) value = tempArray;
if (index = cache.indexOf(value) !== -1) {
// If we have it in cache, report the circle with the key we first found it in
return '[ Circular {' + (keyMap[index] || 'root') + '} ]';
}
cache.push(value);
keyMap.push(key);
}
return value;
});
return string;
}
PhantomReporter.prototype.reportSpecResults = function(spec) {
var _results = spec.results();
var results = {
description : _results.description,
messages : _results.getItems(),
failedCount : _results.failedCount,
totalCount : _results.totalCount,
passedCount : _results.passedCount,
skipped : _results.skipped,
passed : _results.passed(),
msg : _results.failedCount > 0 ? "failed" : "passed"
};
this.results_[spec.id] = results;
// Quick hack to alleviate cyclical object breaking JSONification.
results.messages.forEach(function(item){
if (item.expected) {
item.expected = stringify(item.expected);
}
if (item.actual) {
item.actual = stringify(item.actual);
}
});
phantom.sendMessage( 'jasmine.reportSpecResults', spec.id, results, this.resultsForSpec(spec.id));
};
PhantomReporter.prototype.resultsForSpecs = function(specIds){
var results = {};
for (var i = 0; i < specIds.length; i++) {
var specId = specIds[i];
results[specId] = this.summarizeResult_(this.results_[specId]);
}
return results;
};
PhantomReporter.prototype.summarizeResult_ = function(result){
var summaryMessages = [];
var messagesLength = result.messages.length;
for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
var resultMessage = result.messages[messageIndex];
summaryMessages.push({
text: resultMessage.type === 'log' ? resultMessage.toString() : jasmine.undefined,
passed: resultMessage.passed ? resultMessage.passed() : true,
type: resultMessage.type,
message: resultMessage.message,
trace: {
stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
}
});
}
return {
result : result.result,
messages : summaryMessages
};
};
jasmine.getEnv().addReporter( new PhantomReporter() );
}());

View File

@ -0,0 +1,21 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Jasmine Spec Runner</title>
<% css.forEach(function(style){ %>
<link rel="stylesheet" type="text/css" href="<%= style %>">
<% }) %>
<% with (scripts) { %>
<% [].concat(jasmine, vendor, helpers, src, specs, reporters, start).forEach(function(script){ %>
<script type="text/javascript" src="<%= script %>"></script>
<% }) %>
<% }; %>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,44 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Jasmine Spec Runner</title>
<script src="<%= options.requirejs %>"></script>
<script>
require.config({
baseUrl : '<%= options.baseUrl %>'
});
</script>
<% css.forEach(function(style){ %>
<link rel="stylesheet" type="text/css" href="<%= style %>">
<% }) %>
<% with (scripts) { %>
<% [].concat(jasmine, vendor, helpers).forEach(function(script){ %>
<script type="text/javascript" src="<%= script %>"></script>
<% }) %>
<% }; %>
<script>
require([
<% scripts.src.forEach(function(script, i){ %>
'<%= script.replace(new RegExp('^' + options.baseUrl),"").replace(/\.js$/,"") %>' <% if (i !== scripts.src.length-1){ %>,<% } %>
<% }) %>
],
function(){
require(['<%= [].concat(scripts.specs,scripts.reporters).join("','") %>'], function(){
require(['<%= scripts.start.join("','") %>'], function(){
// good to go!
})
})
}
)
</script>
</head>
<body>
</body>
</html>

67
tasks/lib/jasmine.js Normal file
View File

@ -0,0 +1,67 @@
/*jshint latedef:false, curly:false*/
'use strict';
var grunt = require('grunt'),
path = require('path');
var baseDir = '.';
exports.buildSpecrunner = buildSpecrunner;
exports.getRelativeFileList = getRelativeFileList;
function buildSpecrunner(src, options){
var reporters = [
__dirname + '/../jasmine/reporters/PhantomReporter.js'
];
var jasmineCss = [
__dirname + '/../../vendor/jasmine/lib/jasmine-core/jasmine.css'
];
var jasmineCore = [
__dirname + '/../../vendor/jasmine/lib/jasmine-core/jasmine.js',
__dirname + '/../../vendor/jasmine/lib/jasmine-core/jasmine-html.js'
];
var jasmineHelper = __dirname + '/../jasmine/jasmine-helper.js';
var context = {
css : getRelativeFileList(jasmineCss),
scripts : {
jasmine : getRelativeFileList(jasmineCore),
helpers : getRelativeFileList(options.helpers),
specs : getRelativeFileList(options.specs),
src : getRelativeFileList(src),
vendor : getRelativeFileList(options.vendor),
reporters : getRelativeFileList(reporters),
start : getRelativeFileList(jasmineHelper)
},
options : options.templateOptions || {}
};
var source = '';
grunt.file.copy(options.template, path.join(baseDir,options.outfile), {
process : function(src) {
source = grunt.util._.template(src, context);
return source;
}
});
return source;
}
function getRelativeFileList(/* args... */) {
var list = Array.prototype.slice.call(arguments);
var base = path.resolve(baseDir);
var files = [];
list.forEach(function(listItem){
if (listItem) files = files.concat(grunt.file.expandFiles(listItem));
});
files = grunt.util._(files).map(function(file){
return path.resolve(file).replace(base,'.').replace(/\\/g,'/');
});
return files;
}

View File

@ -0,0 +1,21 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Jasmine Spec Runner</title>
<% css.forEach(function(style){ %>
<link rel="stylesheet" type="text/css" href="<%= style %>">
<% }) %>
<% with (scripts) { %>
<% [].concat(jasmine, vendor, helpers, src, specs, reporters, start).forEach(function(script){ %>
<script type="text/javascript" src="<%= script %>"></script>
<% }) %>
<% }; %>
</head>
<body>
</body>
</html>

53
test/fixtures/pivotal/SpecRunner.html vendored Normal file
View File

@ -0,0 +1,53 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Jasmine Spec Runner</title>
<link rel="shortcut icon" type="image/png" href="lib/jasmine-1.2.0/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="lib/jasmine-1.2.0/jasmine.css">
<script type="text/javascript" src="lib/jasmine-1.2.0/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine-1.2.0/jasmine-html.js"></script>
<!-- include source files here... -->
<script type="text/javascript" src="spec/SpecHelper.js"></script>
<script type="text/javascript" src="spec/PlayerSpec.js"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="src/Player.js"></script>
<script type="text/javascript" src="src/Song.js"></script>
<script type="text/javascript">
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();
</script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,20 @@
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.

View File

@ -0,0 +1,616 @@
jasmine.HtmlReporterHelpers = {};
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) {
el.appendChild(child);
}
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
var results = child.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
return status;
};
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
var parentDiv = this.dom.summary;
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
var parent = child[parentSuite];
if (parent) {
if (typeof this.views.suites[parent.id] == 'undefined') {
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
}
parentDiv = this.views.suites[parent.id].element;
}
parentDiv.appendChild(childElement);
};
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
for(var fn in jasmine.HtmlReporterHelpers) {
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
}
};
jasmine.HtmlReporter = function(_doc) {
var self = this;
var doc = _doc || window.document;
var reporterView;
var dom = {};
// Jasmine Reporter Public Interface
self.logRunningSpecs = false;
self.reportRunnerStarting = function(runner) {
var specs = runner.specs() || [];
if (specs.length == 0) {
return;
}
createReporterDom(runner.env.versionString());
doc.body.appendChild(dom.reporter);
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
reporterView.addSpecs(specs, self.specFilter);
};
self.reportRunnerResults = function(runner) {
reporterView && reporterView.complete();
};
self.reportSuiteResults = function(suite) {
reporterView.suiteComplete(suite);
};
self.reportSpecStarting = function(spec) {
if (self.logRunningSpecs) {
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
self.reportSpecResults = function(spec) {
reporterView.specComplete(spec);
};
self.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
self.specFilter = function(spec) {
if (!focusedSpecName()) {
return true;
}
return spec.getFullName().indexOf(focusedSpecName()) === 0;
};
return self;
function focusedSpecName() {
var specName;
(function memoizeFocusedSpec() {
if (specName) {
return;
}
var paramMap = [];
var params = doc.location.search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
specName = paramMap.spec;
})();
return specName;
}
function createReporterDom(version) {
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
dom.banner = self.createDom('div', { className: 'banner' },
self.createDom('span', { className: 'title' }, "Jasmine "),
self.createDom('span', { className: 'version' }, version)),
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
dom.alert = self.createDom('div', {className: 'alert'}),
dom.results = self.createDom('div', {className: 'results'},
dom.summary = self.createDom('div', { className: 'summary' }),
dom.details = self.createDom('div', { id: 'details' }))
);
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) {
this.startedAt = new Date();
this.runningSpecCount = 0;
this.completeSpecCount = 0;
this.passedCount = 0;
this.failedCount = 0;
this.skippedCount = 0;
this.createResultsMenu = function() {
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
' | ',
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
this.summaryMenuItem.onclick = function() {
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
};
this.detailsMenuItem.onclick = function() {
showDetails();
};
};
this.addSpecs = function(specs, specFilter) {
this.totalSpecCount = specs.length;
this.views = {
specs: {},
suites: {}
};
for (var i = 0; i < specs.length; i++) {
var spec = specs[i];
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
if (specFilter(spec)) {
this.runningSpecCount++;
}
}
};
this.specComplete = function(spec) {
this.completeSpecCount++;
if (isUndefined(this.views.specs[spec.id])) {
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
}
var specView = this.views.specs[spec.id];
switch (specView.status()) {
case 'passed':
this.passedCount++;
break;
case 'failed':
this.failedCount++;
break;
case 'skipped':
this.skippedCount++;
break;
}
specView.refresh();
this.refresh();
};
this.suiteComplete = function(suite) {
var suiteView = this.views.suites[suite.id];
if (isUndefined(suiteView)) {
return;
}
suiteView.refresh();
};
this.refresh = function() {
if (isUndefined(this.resultsMenu)) {
this.createResultsMenu();
}
// currently running UI
if (isUndefined(this.runningAlert)) {
this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
dom.alert.appendChild(this.runningAlert);
}
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
// skipped specs UI
if (isUndefined(this.skippedAlert)) {
this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
}
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.skippedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.skippedAlert);
}
// passing specs UI
if (isUndefined(this.passedAlert)) {
this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
}
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
// failing specs UI
if (isUndefined(this.failedAlert)) {
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
}
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
if (this.failedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.failedAlert);
dom.alert.appendChild(this.resultsMenu);
}
// summary info
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
};
this.complete = function() {
dom.alert.removeChild(this.runningAlert);
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.failedCount === 0) {
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
} else {
showDetails();
}
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
};
return this;
function showDetails() {
if (dom.reporter.className.search(/showDetails/) === -1) {
dom.reporter.className += " showDetails";
}
}
function isUndefined(obj) {
return typeof obj === 'undefined';
}
function isDefined(obj) {
return !isUndefined(obj);
}
function specPluralizedFor(count) {
var str = count + " spec";
if (count > 1) {
str += "s"
}
return str;
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
this.spec = spec;
this.dom = dom;
this.views = views;
this.symbol = this.createDom('li', { className: 'pending' });
this.dom.symbolSummary.appendChild(this.symbol);
this.summary = this.createDom('div', { className: 'specSummary' },
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.description)
);
this.detail = this.createDom('div', { className: 'specDetail' },
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.getFullName())
);
};
jasmine.HtmlReporter.SpecView.prototype.status = function() {
return this.getSpecStatus(this.spec);
};
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
this.symbol.className = this.status();
switch (this.status()) {
case 'skipped':
break;
case 'passed':
this.appendSummaryToSuiteDiv();
break;
case 'failed':
this.appendSummaryToSuiteDiv();
this.appendFailureDetail();
break;
}
};
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
this.summary.className += ' ' + this.status();
this.appendToSummary(this.spec, this.summary);
};
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
this.detail.className += ' ' + this.status();
var resultItems = this.spec.results().getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
this.detail.appendChild(messagesDiv);
this.dom.details.appendChild(this.detail);
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
this.suite = suite;
this.dom = dom;
this.views = views;
this.element = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
);
this.appendToSummary(this.suite, this.element);
};
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
return this.getSpecStatus(this.suite);
};
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
this.element.className += " " + this.status();
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
/* @deprecated Use jasmine.HtmlReporter instead
*/
jasmine.TrivialReporter = function(doc) {
this.document = doc || document;
this.suiteDivs = {};
this.logRunningSpecs = false;
};
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) { el.appendChild(child); }
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
var showPassed, showSkipped;
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
this.createDom('div', { className: 'banner' },
this.createDom('div', { className: 'logo' },
this.createDom('span', { className: 'title' }, "Jasmine"),
this.createDom('span', { className: 'version' }, runner.env.versionString())),
this.createDom('div', { className: 'options' },
"Show ",
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
)
),
this.runnerDiv = this.createDom('div', { className: 'runner running' },
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
);
this.document.body.appendChild(this.outerDiv);
var suites = runner.suites();
for (var i = 0; i < suites.length; i++) {
var suite = suites[i];
var suiteDiv = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
this.suiteDivs[suite.id] = suiteDiv;
var parentDiv = this.outerDiv;
if (suite.parentSuite) {
parentDiv = this.suiteDivs[suite.parentSuite.id];
}
parentDiv.appendChild(suiteDiv);
}
this.startedAt = new Date();
var self = this;
showPassed.onclick = function(evt) {
if (showPassed.checked) {
self.outerDiv.className += ' show-passed';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
}
};
showSkipped.onclick = function(evt) {
if (showSkipped.checked) {
self.outerDiv.className += ' show-skipped';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
}
};
};
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
var results = runner.results();
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
this.runnerDiv.setAttribute("class", className);
//do it twice for IE
this.runnerDiv.setAttribute("className", className);
var specs = runner.specs();
var specCount = 0;
for (var i = 0; i < specs.length; i++) {
if (this.specFilter(specs[i])) {
specCount++;
}
}
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
};
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
var results = suite.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.totalCount === 0) { // todo: change this to check results.skipped
status = 'skipped';
}
this.suiteDivs[suite.id].className += " " + status;
};
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
if (this.logRunningSpecs) {
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
var results = spec.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
var specDiv = this.createDom('div', { className: 'spec ' + status },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(spec.getFullName()),
title: spec.getFullName()
}, spec.description));
var resultItems = results.getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
specDiv.appendChild(messagesDiv);
}
this.suiteDivs[spec.suite.id].appendChild(specDiv);
};
jasmine.TrivialReporter.prototype.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
jasmine.TrivialReporter.prototype.getLocation = function() {
return this.document.location;
};
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
var paramMap = {};
var params = this.getLocation().search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
if (!paramMap.spec) {
return true;
}
return spec.getFullName().indexOf(paramMap.spec) === 0;
};

View File

@ -0,0 +1,81 @@
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
#HTMLReporter a { text-decoration: none; }
#HTMLReporter a:hover { text-decoration: underline; }
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
#HTMLReporter .version { color: #aaaaaa; }
#HTMLReporter .banner { margin-top: 14px; }
#HTMLReporter .duration { color: #aaaaaa; float: right; }
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
#HTMLReporter .runningAlert { background-color: #666666; }
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
#HTMLReporter .passingAlert { background-color: #a6b779; }
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
#HTMLReporter .failingAlert { background-color: #cf867e; }
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
#HTMLReporter .results { margin-top: 14px; }
#HTMLReporter #details { display: none; }
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter.showDetails .summary { display: none; }
#HTMLReporter.showDetails #details { display: block; }
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter .summary { margin-top: 14px; }
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
#HTMLReporter .description + .suite { margin-top: 0; }
#HTMLReporter .suite { margin-top: 14px; }
#HTMLReporter .suite a { color: #333333; }
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
#HTMLReporter .resultMessage span.result { display: block; }
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
#TrivialReporter .runner.running { background-color: yellow; }
#TrivialReporter .options { text-align: right; font-size: .8em; }
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
#TrivialReporter .suite .suite { margin: 5px; }
#TrivialReporter .suite.passed { background-color: #dfd; }
#TrivialReporter .suite.failed { background-color: #fdd; }
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
#TrivialReporter .spec.skipped { background-color: #bbb; }
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
#TrivialReporter .passed { background-color: #cfc; display: none; }
#TrivialReporter .failed { background-color: #fbb; }
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
#TrivialReporter .resultMessage .mismatch { color: black; }
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
describe("Player", function() {
var player;
var song;
beforeEach(function() {
player = new Player();
song = new Song();
});
it("should be able to play a Song", function() {
player.play(song);
expect(player.currentlyPlayingSong).toEqual(song);
//demonstrates use of custom matcher
expect(player).toBePlaying(song);
});
describe("when song has been paused", function() {
beforeEach(function() {
player.play(song);
player.pause();
});
it("should indicate that the song is currently paused", function() {
expect(player.isPlaying).toBeFalsy();
// demonstrates use of 'not' with a custom matcher
expect(player).not.toBePlaying(song);
});
it("should be possible to resume", function() {
player.resume();
expect(player.isPlaying).toBeTruthy();
expect(player.currentlyPlayingSong).toEqual(song);
});
});
// demonstrates use of spies to intercept and test method calls
it("tells the current song if the user has made it a favorite", function() {
spyOn(song, 'persistFavoriteStatus');
player.play(song);
player.makeFavorite();
expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
});
//demonstrates use of expected exceptions
describe("#resume", function() {
it("should throw an exception if song is already playing", function() {
player.play(song);
expect(function() {
player.resume();
}).toThrow("song is already playing");
});
});
});

View File

@ -0,0 +1,9 @@
beforeEach(function() {
this.addMatchers({
toBePlaying: function(expectedSong) {
var player = this.actual;
return player.currentlyPlayingSong === expectedSong &&
player.isPlaying;
}
});
});

22
test/fixtures/pivotal/src/Player.js vendored Normal file
View File

@ -0,0 +1,22 @@
function Player() {
}
Player.prototype.play = function(song) {
this.currentlyPlayingSong = song;
this.isPlaying = true;
};
Player.prototype.pause = function() {
this.isPlaying = false;
};
Player.prototype.resume = function() {
if (this.isPlaying) {
throw new Error("song is already playing");
}
this.isPlaying = true;
};
Player.prototype.makeFavorite = function() {
this.currentlyPlayingSong.persistFavoriteStatus(true);
};

7
test/fixtures/pivotal/src/Song.js vendored Normal file
View File

@ -0,0 +1,7 @@
function Song() {
}
Song.prototype.persistFavoriteStatus = function(value) {
// something complicated
throw new Error("not yet implemented");
};

10
test/fixtures/requirejs/spec/SumSpec.js vendored Normal file
View File

@ -0,0 +1,10 @@
describe('Example AMD test', function(){
var math = require('math');
describe('Sum', function(){
it('Should add two numbers together', function(){
expect(math.sum(2,10)).toEqual(12);
})
})
});

5
test/fixtures/requirejs/src/math.js vendored Normal file
View File

@ -0,0 +1,5 @@
define(['sum'],function(sum){
return {
sum : sum
}
})

5
test/fixtures/requirejs/src/sum.js vendored Normal file
View File

@ -0,0 +1,5 @@
define(function(){
return function(a,b){
return a + b;
}
})

View File

@ -4,7 +4,8 @@ var grunt = require('grunt');
exports.jasmine = {
request: function(test) {
test.expect(2);
test.expect(0);
test.done();
}
};

2
vendor/jasmine/.rspec vendored Normal file
View File

@ -0,0 +1,2 @@
--color
--format Fuubar

45
vendor/jasmine/Contribute.markdown vendored Normal file
View File

@ -0,0 +1,45 @@
# Developing for Jasmine Core
## How to Contribute
We welcome your contributions - Thanks for helping make Jasmine a better project for everyone. Please review the backlog and discussion lists (the main group - [http://groups.google.com/group/jasmine-js](http://groups.google.com/group/jasmine-js) and the developer's list - [http://groups.google.com/group/jasmine-js-dev](http://groups.google.com/group/jasmine-js-dev)) before starting work - what you're looking for may already have been done. If it hasn't, the community can help make your contribution better.
## How to write new Jasmine code
Or, How to make a successful pull request
* _Do not change the public interface_. Lots of projects depend on Jasmine and if you aren't careful you'll break them
* _Be environment agnostic_ - server-side developers are just as important as browser developers
* _Be browser agnostic_ - if you must rely on browser-specific functionality, please write it in a way that degrades gracefully
* _Write specs_ - Jasmine's a testing framework; don't add functionality without test-driving it
* _Ensure the *entire* test suite is green_ in all the big browsers, Node, and JSHint - your contribution shouldn't break Jasmine for other users
Follow these tips and your pull request, patch, or suggestion is much more likely to be integrated.
## Environment
Ruby, RubyGems and Rake are used in order to script the various file interactions. You will need to run on a system that supports Ruby in order to run Jasmine's specs.
Node.js is used to run most of the specs (the HTML-independent code) and should be present. Additionally, the JS Hint project scrubs the source code as part of the spec process.
## Development
All source code belongs in `src/`. The `core/` directory contains the bulk of Jasmine's functionality. This code should remain browser- and environment-agnostic. If your feature or fix cannot be, as mentioned above, please degrade gracefully. Any code that should only be in a non-browser environment should live in `src/console/`. Any code that depends on a browser (specifically, it expects `window` to be the global or `document` is present) should live in `src/html/`.
Please respect the code patterns as possible. For example, using `jasmine.getGlobal()` to get the global object so as to remain environment agnostic.
## Running Specs
As in all good projects, the `spec/` directory mirrors `src/` and follows the same rules. The browser runner will include and attempt to run all specs. The node runner will exclude any html-dependent specs (those in `spec/html/`).
You will notice that all specs are run against the built `jasmine.js` instead of the component source files. This is intentional as a way to ensure that the concatenation code is working correctly.
Please ensure all specs are green before committing or issuing a pull request.
There are Thor tasks to help with getting green - run `thor list` to see them all. Here are the key tasks:
* `thor jasmine_dev:execute_specs` outputs the expected number of specs that should be run and attempts to run in browser and Node
* `thor jasmine_dev:execute_specs_in_browser` opens `spec/runner.html` in the default browser on MacOS. Please run this in at least Firefox and Chrome before committing
* `thor jasmine_dev:execute_specs_in_node` runs all the Jasmine specs in Node.js - it will complain if Node is not installed
* `thor jasmine_dev:js_hint` runs all the files through JSHint and will complain about potential viable issues with your code. Fix them.

3
vendor/jasmine/Gemfile vendored Normal file
View File

@ -0,0 +1,3 @@
source :rubygems
gem "rake"
gemspec

20
vendor/jasmine/MIT.LICENSE vendored Normal file
View File

@ -0,0 +1,20 @@
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.

24
vendor/jasmine/README.markdown vendored Normal file
View File

@ -0,0 +1,24 @@
<a name="README">[Jasmine](http://pivotal.github.com/jasmine/)</a>
=======
**A JavaScript Testing Framework**
Jasmine is a Behavior Driven Development testing framework for JavaScript. It does not rely on browsers, DOM, or any JavaScript framework. Thus it's suited for websites, [Node.js](http://nodejs.org) projects, or anywhere that JavaScript can run.
Documentation & guides live here: [http://pivotal.github.com/jasmine/](http://pivotal.github.com/jasmine/)
## Support
* Search past discussions: [http://groups.google.com/group/jasmine-js](http://groups.google.com/group/jasmine-js)
* Send an email to the list: [jasmine-js@googlegroups.com](jasmine-js@googlegroups.com)
* View the project backlog at Pivotal Tracker: [http://www.pivotaltracker.com/projects/10606](http://www.pivotaltracker.com/projects/10606)
* Follow us on Twitter: [@JasmineBDD](http://twitter.com/JasmineBDD)
## Maintainers
* [Davis W. Frank](mailto:dwfrank@pivotallabs.com), Pivotal Labs
* [Rajan Agaskar](mailto:rajan@pivotallabs.com), Pivotal Labs
* [Christian Williams](mailto:antixian666@gmail.com), Square
Copyright (c) 2008-2012 Pivotal Labs. This software is licensed under the MIT License.

44
vendor/jasmine/Rakefile vendored Normal file
View File

@ -0,0 +1,44 @@
require "bundler"
Bundler::GemHelper.install_tasks
require "json"
require "tilt"
Dir["#{File.dirname(__FILE__)}/tasks/**/*.rb"].each do |file|
require file
end
desc "Run all Jasmine JS specs"
task :jasmine_specs do
jasmine_dev = JasmineDev.new
return unless jasmine_dev.node_installed?
system "thor jasmine_dev:execute_specs"
puts "\n\033[33m>>> DEPRECATED <<< Run Jasmine's JavaScript specs with 'thor jasmine_dev:execute_specs'\n\033[0m"
end
desc "Run all Jasmine core tests (JavaScript and dev tasks)"
task :spec => :require_pages_submodule do
jasmine_dev = JasmineDev.new
return unless jasmine_dev.node_installed?
system "rspec"
end
task :require_pages_submodule do
jasmine_dev = JasmineDev.new
unless jasmine_dev.pages_submodule_installed?
puts 'Installing the Github pages submodule:'
system 'git submodule update --init'
puts 'Now continuing...'
end
end
desc "View full development tasks"
task :list_dev_tasks do
puts "Jasmine uses Thor for command line tasks for development. Here is the command set:"
system "thor list"
end

42
vendor/jasmine/Release.markdown vendored Normal file
View File

@ -0,0 +1,42 @@
# How to work on a Jasmine Release
## Development
___Jasmine Core Maintainers Only___
Follow the instructions in `Contribute.markdown` during development.
### Git Rules
Please work on feature branches.
Please attempt to keep commits to `master` small, but cohesive. If a feature is contained in a bunch of small commits (e.g., it has several wip commits), please squash them when merging back to `master`.
### Version
We attempt to stick to [Semantic Versioning](). Most of the time, development should be against a new minor version - fixing bugs and adding new features that are backwards compatible.
The current version lives in the file `src/version.json`. This file should be set to the version that is _currently_ under development. That is, if version 1.0.0 is the current release then version should be incremented say, to 1.1.0.
This version is used by both `jasmine.js` and the `jasmine-core` Ruby gem.
Note that Jasmine should *not* use the "patch" version number. Let downstream projects rev their patch versions as needed, keeping their major and minor version numbers in sync with Jasmine core.
### Update the Github Pages (as needed)
Github pages have to exist in a branch called `gh-pages` in order for their app to serve them. This repo adds that branch as a submodule under the `pages` directory. This is a bit of a hack, but it allows us to work with the pages and the source at the same time and with one set of rake tasks.
If you want to submit changes to this repo and aren't a Pivotal Labs employee, you can fork and work in the `gh-pages` branch. You won't be able to edit the pages in the submodule off of master.
The pages are built with [Frank](https://github.com/blahed/frank). All the source for these pages live in the `pages/pages_source` directory.
## Release
When ready to release - specs are all green and the stories are done:
1. Update the version in `version.json` to a release candidate - add a `release_candidate` property with a value of 1
1. Update any comments on the public interfaces
1. Update any links or top-level landing page for the Github Pages
1. `thor jasmine_dev:release_prep` - updates the version, builds the `.js` files, builds the standalone release, and builds the Github pages
1. `rake release` - tags the repo with the version, builds the `jasmine-core` gem, pushes the gem to Rubygems.org
There should be a post to Pivotal Labs blog and a tweet to that link.

15
vendor/jasmine/SpecRunner.html vendored Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Jasmine Spec Runner</title>
<link rel="stylesheet" type="text/css" href="lib/jasmine-core/jasmine.css">
<script type="text/javascript" src="lib/jasmine-core/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine-core/jasmine-html.js"></script>
</head>
<body>
</body>
</html>

29
vendor/jasmine/config.rb vendored Normal file
View File

@ -0,0 +1,29 @@
#
# Compass configuration file - for building Jasmine's 'final CSS files
#
# Require any additional compass plugins here.
# Set this to the root of your project when deployed:
http_path = "/"
css_dir = "src/html"
sass_dir = "src/html"
images_dir = "images"
javascripts_dir = "javascripts"
# You can select your preferred output style here (can be overridden via the command line):
# output_style = :expanded or :nested or :compact or :compressed
output_style = :compact
# To enable relative paths to assets via compass helper functions. Uncomment:
# relative_assets = true
# To disable debugging comments that display the original location of your selectors. Uncomment:
line_comments = false
# If you prefer the indented syntax, you might want to regenerate this
# project again passing --syntax sass, or you can uncomment this:
# preferred_syntax = :sass
# and then run:
# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

32
vendor/jasmine/jasmine-core.gemspec vendored Normal file
View File

@ -0,0 +1,32 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "jasmine-core/version"
Gem::Specification.new do |s|
s.name = "jasmine-core"
s.version = Jasmine::Core::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["Rajan Agaskar", "Davis W. Frank", "Christian Williams"]
s.summary = %q{JavaScript BDD framework}
s.description = %q{Test your JavaScript without any framework dependencies, in any environment, and with a nice descriptive syntax.}
s.email = %q{jasmine-js@googlegroups.com}
s.homepage = "http://pivotal.github.com/jasmine"
s.rubyforge_project = "jasmine-core"
s.license = "MIT"
s.files = Dir.glob("./lib/**/*") + Dir.glob("./lib/jasmine-core/spec/**/*.js")
s.require_paths = ["lib"]
s.add_development_dependency "json_pure", ">= 1.4.3"
s.add_development_dependency "tilt"
s.add_development_dependency "sass"
s.add_development_dependency "compass"
s.add_development_dependency "ragaskar-jsdoc_helper"
s.add_development_dependency "rspec"
s.add_development_dependency "fuubar"
s.add_development_dependency "awesome_print"
s.add_development_dependency "thor"
s.add_development_dependency "nokogiri"
s.add_development_dependency "redcarpet", "1.7"
s.add_development_dependency "rocco"
s.add_development_dependency "rdiscount"
end

1
vendor/jasmine/jasmine_dev.thor vendored Normal file
View File

@ -0,0 +1 @@
require "#{File.expand_path(File.dirname(__FILE__))}/tasks/jasmine_dev"

View File

@ -0,0 +1,17 @@
<div align="center">{+new Link().toFile("index.html").withText("Class Index")+}
| {+new Link().toFile("files.html").withText("File Index")+}</div>
<hr />
<h2>Classes</h2>
<ul class="classList">
<for each="thisClass" in="data">
<li>{!
if (thisClass.alias == "_global_") {
output += "<i>"+new Link().toClass(thisClass.alias)+"</i>";
}
else {
output += new Link().toClass(thisClass.alias);
}
!}</li>
</for>
</ul>
<hr />

View File

@ -0,0 +1,56 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset={+IO.encoding+}"" />
{! Link.base = ""; /* all generated links will be relative to this */ !}
<title>JsDoc Reference - File Index</title>
<meta name="generator" content="JsDoc Toolkit" />
<style type="text/css">
{+include("static/default.css")+}
</style>
</head>
<body>
{+include("static/header.html")+}
<div id="index">
{+publish.classesIndex+}
</div>
<div id="content">
<h1 class="classTitle">File Index</h1>
<for each="item" in="data">
<div>
<h2>{+new Link().toSrc(item.alias).withText(item.name)+}</h2>
<if test="item.desc">{+resolveLinks(summarize(item.desc))+}</if>
<dl>
<if test="item.author">
<dt class="heading">Author:</dt>
<dd>{+item.author+}</dd>
</if>
<if test="item.version">
<dt class="heading">Version:</dt>
<dd>{+item.version+}</dd>
</if>
{! var locations = item.comment.getTag('location').map(function($){return $.toString().replace(/(^\$ ?| ?\$$)/g, '').replace(/^HeadURL: https:/g, 'http:');}) !}
<if test="locations.length">
<dt class="heading">Location:</dt>
<for each="location" in="locations">
<dd><a href="{+location+}">{+location+}</a></dd>
</for>
</if>
</dl>
</div>
<hr />
</for>
</div>
<div class="fineprint" style="clear:both">
<if test="JSDOC.opt.D.copyright">&copy;{+JSDOC.opt.D.copyright+}<br /></if>
Documentation generated by <a href="http://www.jsdoctoolkit.org/" target="_blankt">JsDoc Toolkit</a> {+JSDOC.VERSION+} on {+new Date()+}
</div>
</body>
</html>

646
vendor/jasmine/jsdoc-template/class.tmpl vendored Normal file
View File

@ -0,0 +1,646 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset={+IO.encoding+}" />
<meta name="generator" content="JsDoc Toolkit" />
{! Link.base = "../"; /* all generated links will be relative to this */ !}
<title>JsDoc Reference - {+data.alias+}</title>
<style type="text/css">
{+include("static/default.css")+}
</style>
</head>
<body>
<!-- ============================== header ================================= -->
<!-- begin static/header.html -->
{+include("static/header.html")+}
<!-- end static/header.html -->
<!-- ============================== classes index ============================ -->
<div id="index">
<!-- begin publish.classesIndex -->
{+publish.classesIndex+}
<!-- end publish.classesIndex -->
</div>
<div id="content">
<!-- ============================== class title ============================ -->
<h1 class="classTitle">
{!
var classType = "";
if (data.isBuiltin()) {
classType += "Built-In ";
}
if (data.isNamespace) {
if (data.is('FUNCTION')) {
classType += "Function ";
}
classType += "Namespace ";
}
else {
classType += "Class ";
}
!}
{+classType+}{+data.alias+}
</h1>
<!-- ============================== class summary ========================== -->
<p class="description">
<if test="data.augments.length"><br />Extends
{+
data.augments
.sort()
.map(
function($) { return new Link().toSymbol($); }
)
.join(", ")
+}.<br />
</if>
{+resolveLinks(data.classDesc)+}
<if test="!data.isBuiltin()">{# isn't defined in any file #}
<br /><i>Defined in: </i> {+new Link().toSrc(data.srcFile)+}.
</if>
</p>
<!-- ============================== constructor summary ==================== -->
<if test="!data.isBuiltin() && (data.isNamespace || data.is('CONSTRUCTOR'))">
<table class="summaryTable" cellspacing="0" summary="A summary of the constructor documented in the class {+data.alias+}.">
<caption>{+classType+}Summary</caption>
<thead>
<tr>
<th scope="col">Constructor Attributes</th>
<th scope="col">Constructor Name and Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="attributes">{!
if (data.isPrivate) output += "&lt;private&gt; ";
if (data.isInner) output += "&lt;inner&gt; ";
!}&nbsp;</td>
<td class="nameDescription" {!if (data.comment.getTag("hilited").length){output += 'style="color: red"'}!}>
<div class="fixedFont">
<b>{+ new Link().toSymbol(data.alias).inner('constructor')+}</b><if test="classType != 'Namespace '">{+ makeSignature(data.params) +}</if>
</div>
<div class="description">{+resolveLinks(summarize(data.desc))+}</div>
</td>
</tr>
</tbody>
</table>
</if>
<!-- ============================== properties summary ===================== -->
<if test="data.properties.length">
{! var ownProperties = data.properties.filter(function($){return $.memberOf == data.alias && !$.isNamespace}).sort(makeSortby("name")); !}
<if test="ownProperties.length">
<table class="summaryTable" cellspacing="0" summary="A summary of the fields documented in the class {+data.alias+}.">
<caption>Field Summary</caption>
<thead>
<tr>
<th scope="col">Field Attributes</th>
<th scope="col">Field Name and Description</th>
</tr>
</thead>
<tbody>
<for each="member" in="ownProperties">
<tr>
<td class="attributes">{!
if (member.isPrivate) output += "&lt;private&gt; ";
if (member.isInner) output += "&lt;inner&gt; ";
if (member.isStatic) output += "&lt;static&gt; ";
if (member.isConstant) output += "&lt;constant&gt; ";
!}&nbsp;</td>
<td class="nameDescription">
<div class="fixedFont">
<if test="member.isStatic && member.memberOf != '_global_'">{+member.memberOf+}.</if><b>{+new Link().toSymbol(member.alias).withText(member.name)+}</b>
</div>
<div class="description">{+resolveLinks(summarize(member.desc))+}</div>
</td>
</tr>
</for>
</tbody>
</table>
</if>
<if test="data.inheritsFrom.length">
<dl class="inheritsList">
{!
var borrowedMembers = data.properties.filter(function($) {return $.memberOf != data.alias});
var contributers = [];
borrowedMembers.map(function($) {if (contributers.indexOf($.memberOf) < 0) contributers.push($.memberOf)});
for (var i = 0, l = contributers.length; i < l; i++) {
output +=
"<dt>Fields borrowed from class "+new Link().toSymbol(contributers[i])+": </dt>"
+
"<dd>" +
borrowedMembers
.filter(
function($) { return $.memberOf == contributers[i] }
)
.sort(makeSortby("name"))
.map(
function($) { return new Link().toSymbol($.alias).withText($.name) }
)
.join(", ")
+
"</dd>";
}
!}
</dl>
</if>
</if>
<!-- ============================== methods summary ======================== -->
<if test="data.methods.length">
{! var ownMethods = data.methods.filter(function($){return $.memberOf == data.alias && !$.isNamespace}).sort(makeSortby("name")); !}
<if test="ownMethods.length">
<table class="summaryTable" cellspacing="0" summary="A summary of the methods documented in the class {+data.alias+}.">
<caption>Method Summary</caption>
<thead>
<tr>
<th scope="col">Method Attributes</th>
<th scope="col">Method Name and Description</th>
</tr>
</thead>
<tbody>
<for each="member" in="ownMethods">
<tr>
<td class="attributes">{!
if (member.isPrivate) output += "&lt;private&gt; ";
if (member.isInner) output += "&lt;inner&gt; ";
if (member.isStatic) output += "&lt;static&gt; ";
!}&nbsp;</td>
<td class="nameDescription">
<div class="fixedFont"><if test="member.isStatic && member.memberOf != '_global_'">{+member.memberOf+}.</if><b>{+new Link().toSymbol(member.alias).withText(member.name)+}</b>{+makeSignature(member.params)+}
</div>
<div class="description">{+resolveLinks(summarize(member.desc))+}</div>
</td>
</tr>
</for>
</tbody>
</table>
</if>
<if test="data.inheritsFrom.length">
<dl class="inheritsList">
{!
var borrowedMembers = data.methods.filter(function($) {return $.memberOf != data.alias});
var contributers = [];
borrowedMembers.map(function($) {if (contributers.indexOf($.memberOf) < 0) contributers.push($.memberOf)});
for (var i = 0, l = contributers.length; i < l; i++) {
output +=
"<dt>Methods borrowed from class "+new Link().toSymbol(contributers[i])+": </dt>"
+
"<dd>" +
borrowedMembers
.filter(
function($) { return $.memberOf == contributers[i] }
)
.sort(makeSortby("name"))
.map(
function($) { return new Link().toSymbol($.alias).withText($.name) }
)
.join(", ")
+
"</dd>";
}
!}
</dl>
</if>
</if>
<!-- ============================== events summary ======================== -->
<if test="data.events.length">
{! var ownEvents = data.events.filter(function($){return $.memberOf == data.alias && !$.isNamespace}).sort(makeSortby("name")); !}
<if test="ownEvents.length">
<table class="summaryTable" cellspacing="0" summary="A summary of the events documented in the class {+data.alias+}.">
<caption>Event Summary</caption>
<thead>
<tr>
<th scope="col">Event Attributes</th>
<th scope="col">Event Name and Description</th>
</tr>
</thead>
<tbody>
<for each="member" in="ownEvents">
<tr>
<td class="attributes">{!
if (member.isPrivate) output += "&lt;private&gt; ";
if (member.isInner) output += "&lt;inner&gt; ";
if (member.isStatic) output += "&lt;static&gt; ";
!}&nbsp;</td>
<td class="nameDescription">
<div class="fixedFont"><if test="member.isStatic && member.memberOf != '_global_'">{+member.memberOf+}.</if><b>{+new Link().toSymbol(member.alias).withText(member.name)+}</b>{+makeSignature(member.params)+}
</div>
<div class="description">{+resolveLinks(summarize(member.desc))+}</div>
</td>
</tr>
</for>
</tbody>
</table>
</if>
<if test="data.inheritsFrom.length">
<dl class="inheritsList">
{!
var borrowedMembers = data.events.filter(function($) {return $.memberOf != data.alias});
var contributers = [];
borrowedMembers.map(function($) {if (contributers.indexOf($.memberOf) < 0) contributers.push($.memberOf)});
for (var i = 0, l = contributers.length; i < l; i++) {
output +=
"<dt>Events borrowed from class "+new Link().toSymbol(contributers[i])+": </dt>"
+
"<dd>" +
borrowedMembers
.filter(
function($) { return $.memberOf == contributers[i] }
)
.sort(makeSortby("name"))
.map(
function($) { return new Link().toSymbol($.alias).withText($.name) }
)
.join(", ")
+
"</dd>";
}
!}
</dl>
</if>
</if>
<!-- ============================== constructor details ==================== -->
<if test="!data.isBuiltin() && (data.isNamespace || data.is('CONSTRUCTOR'))">
<div class="details"><a name="constructor"> </a>
<div class="sectionTitle">
{+classType+}Detail
</div>
<div class="fixedFont">{!
if (data.isPrivate) output += "&lt;private&gt; ";
if (data.isInner) output += "&lt;inner&gt; ";
!}
<b>{+ data.alias +}</b><if test="classType != 'Namespace '">{+ makeSignature(data.params) +}</if>
</div>
<div class="description">
{+resolveLinks(data.desc)+}
<if test="data.author"><br /><i>Author: </i>{+data.author+}.</if>
</div>
<if test="data.example.length">
<for each="example" in="data.example">
<pre class="code">{+example+}</pre>
</for>
</if>
<if test="data.params.length">
<dl class="detailList">
<dt class="heading">Parameters:</dt>
<for each="item" in="data.params">
<dt>
{+((item.type)?""+("<span class=\"light fixedFont\">{"+(new Link().toSymbol(item.type)+"}</span> ")) : "")+} <b>{+item.name+}</b>
<if test="item.isOptional"><i>Optional<if test="item.defaultValue">, Default: {+item.defaultValue+}</if></i></if>
</dt>
<dd>{+resolveLinks(item.desc)+}</dd>
</for>
</dl>
</if>
<if test="data.deprecated">
<dl class="detailList">
<dt class="heading">Deprecated:</dt>
<dt>
{+resolveLinks(data.deprecated)+}
</dt>
</dl>
</if>
<if test="data.since">
<dl class="detailList">
<dt class="heading">Since:</dt>
<dd>{+ data.since +}</dd>
</dl>
</if>
<if test="data.exceptions.length">
<dl class="detailList">
<dt class="heading">Throws:</dt>
<for each="item" in="data.exceptions">
<dt>
{+((item.type)?"<span class=\"light fixedFont\">{"+(new Link().toSymbol(item.type))+"}</span> " : "")+} <b>{+item.name+}</b>
</dt>
<dd>{+resolveLinks(item.desc)+}</dd>
</for>
</dl>
</if>
<if test="data.returns.length">
<dl class="detailList">
<dt class="heading">Returns:</dt>
<for each="item" in="data.returns">
<dd>{+((item.type)?"<span class=\"light fixedFont\">{"+(new Link().toSymbol(item.type))+"}</span> " : "")+}{+resolveLinks(item.desc)+}</dd>
</for>
</dl>
</if>
<if test="data.requires.length">
<dl class="detailList">
<dt class="heading">Requires:</dt>
<for each="item" in="data.requires">
<dd>{+ resolveLinks(item) +}</dd>
</for>
</dl>
</if>
<if test="data.see.length">
<dl class="detailList">
<dt class="heading">See:</dt>
<for each="item" in="data.see">
<dd>{+ new Link().toSymbol(item) +}</dd>
</for>
</dl>
</if>
</div>
</if>
<!-- ============================== field details ========================== -->
<if test="defined(ownProperties) && ownProperties.length">
<div class="sectionTitle">
Field Detail
</div>
<for each="member" in="ownProperties">
<a name="{+Link.symbolNameToLinkName(member)+}"> </a>
<div class="fixedFont">{!
if (member.isPrivate) output += "&lt;private&gt; ";
if (member.isInner) output += "&lt;inner&gt; ";
if (member.isStatic) output += "&lt;static&gt; ";
if (member.isConstant) output += "&lt;constant&gt; ";
!}
<if test="member.type"><span class="light">{{+new Link().toSymbol(member.type)+}}</span></if>
<if test="member.isStatic && member.memberOf != '_global_'"><span class="light">{+member.memberOf+}.</span></if><b>{+member.name+}</b>
</div>
<div class="description">
{+resolveLinks(member.desc)+}
<if test="member.srcFile != data.srcFile">
<br />
<i>Defined in: </i> {+new Link().toSrc(member.srcFile)+}.
</if>
<if test="member.author"><br /><i>Author: </i>{+member.author+}.</if>
</div>
<if test="member.example.length">
<for each="example" in="member.example">
<pre class="code">{+example+}</pre>
</for>
</if>
<if test="member.deprecated">
<dl class="detailList">
<dt class="heading">Deprecated:</dt>
<dt>
{+ member.deprecated +}
</dt>
</dl>
</if>
<if test="member.since">
<dl class="detailList">
<dt class="heading">Since:</dt>
<dd>{+ member.since +}</dd>
</dl>
</if>
<if test="member.see.length">
<dl class="detailList">
<dt class="heading">See:</dt>
<for each="item" in="member.see">
<dd>{+ new Link().toSymbol(item) +}</dd>
</for>
</dl>
</if>
<if test="member.defaultValue">
<dl class="detailList">
<dt class="heading">Default Value:</dt>
<dd>
{+resolveLinks(member.defaultValue)+}
</dd>
</dl>
</if>
<if test="!$member_last"><hr /></if>
</for>
</if>
<!-- ============================== method details ========================= -->
<if test="defined(ownMethods) && ownMethods.length">
<div class="sectionTitle">
Method Detail
</div>
<for each="member" in="ownMethods">
<a name="{+Link.symbolNameToLinkName(member)+}"> </a>
<div class="fixedFont">{!
if (member.isPrivate) output += "&lt;private&gt; ";
if (member.isInner) output += "&lt;inner&gt; ";
if (member.isStatic) output += "&lt;static&gt; ";
!}
<if test="member.type"><span class="light">{{+new Link().toSymbol(member.type)+}}</span></if>
<if test="member.isStatic && member.memberOf != '_global_'"><span class="light">{+member.memberOf+}.</span></if><b>{+member.name+}</b>{+makeSignature(member.params)+}
</div>
<div class="description">
{+resolveLinks(member.desc)+}
<if test="member.srcFile != data.srcFile">
<br />
<i>Defined in: </i> {+new Link().toSrc(member.srcFile)+}.
</if>
<if test="member.author"><br /><i>Author: </i>{+member.author+}.</if>
</div>
<if test="member.example.length">
<for each="example" in="member.example">
<pre class="code">{+example+}</pre>
</for>
</if>
<if test="member.params.length">
<dl class="detailList">
<dt class="heading">Parameters:</dt>
<for each="item" in="member.params">
<dt>
{+((item.type)?"<span class=\"light fixedFont\">{"+(new Link().toSymbol(item.type))+"}</span> " : "")+}<b>{+item.name+}</b>
<if test="item.isOptional"><i>Optional<if test="item.defaultValue">, Default: {+item.defaultValue+}</if></i></if>
</dt>
<dd>{+resolveLinks(item.desc)+}</dd>
</for>
</dl>
</if>
<if test="member.deprecated">
<dl class="detailList">
<dt class="heading">Deprecated:</dt>
<dt>
{+member.deprecated+}
</dt>
</dl>
</if>
<if test="member.since">
<dl class="detailList">
<dt class="heading">Since:</dt>
<dd>{+ member.since +}</dd>
</dl>
</dl>
</if>
<if test="member.exceptions.length">
<dl class="detailList">
<dt class="heading">Throws:</dt>
<for each="item" in="member.exceptions">
<dt>
{+((item.type)?"<span class=\"light fixedFont\">{"+(new Link().toSymbol(item.type))+"}</span> " : "")+} <b>{+item.name+}</b>
</dt>
<dd>{+resolveLinks(item.desc)+}</dd>
</for>
</dl>
</if>
<if test="member.returns.length">
<dl class="detailList">
<dt class="heading">Returns:</dt>
<for each="item" in="member.returns">
<dd>{+((item.type)?"<span class=\"light fixedFont\">{"+(new Link().toSymbol(item.type))+"}</span> " : "")+}{+resolveLinks(item.desc)+}</dd>
</for>
</dl>
</if>
<if test="member.requires.length">
<dl class="detailList">
<dt class="heading">Requires:</dt>
<for each="item" in="member.requires">
<dd>{+ resolveLinks(item) +}</dd>
</for>
</dl>
</if>
<if test="member.see.length">
<dl class="detailList">
<dt class="heading">See:</dt>
<for each="item" in="member.see">
<dd>{+ new Link().toSymbol(item) +}</dd>
</for>
</dl>
</if>
<if test="!$member_last"><hr /></if>
</for>
</if>
<!-- ============================== event details ========================= -->
<if test="defined(ownEvents) && ownEvents.length">
<div class="sectionTitle">
Event Detail
</div>
<for each="member" in="ownEvents">
<a name="event:{+Link.symbolNameToLinkName(member)+}"> </a>
<div class="fixedFont">{!
if (member.isPrivate) output += "&lt;private&gt; ";
if (member.isInner) output += "&lt;inner&gt; ";
if (member.isStatic) output += "&lt;static&gt; ";
!}
<if test="member.type"><span class="light">{{+new Link().toSymbol(member.type)+}}</span></if>
<if test="member.isStatic && member.memberOf != '_global_'"><span class="light">{+member.memberOf+}.</span></if><b>{+member.name+}</b>{+makeSignature(member.params)+}
</div>
<div class="description">
{+resolveLinks(member.desc)+}
<if test="member.srcFile != data.srcFile">
<br />
<i>Defined in: </i> {+new Link().toSrc(member.srcFile)+}.
</if>
<if test="member.author"><br /><i>Author: </i>{+member.author+}.</if>
</div>
<if test="member.example.length">
<for each="example" in="member.example">
<pre class="code">{+example+}</pre>
</for>
</if>
<if test="member.params.length">
<dl class="detailList">
<dt class="heading">Parameters:</dt>
<for each="item" in="member.params">
<dt>
{+((item.type)?"<span class=\"light fixedFont\">{"+(new Link().toSymbol(item.type))+"}</span> " : "")+}<b>{+item.name+}</b>
<if test="item.isOptional"><i>Optional<if test="item.defaultValue">, Default: {+item.defaultValue+}</if></i></if>
</dt>
<dd>{+resolveLinks(item.desc)+}</dd>
</for>
</dl>
</if>
<if test="member.deprecated">
<dl class="detailList">
<dt class="heading">Deprecated:</dt>
<dt>
{+member.deprecated+}
</dt>
</dl>
</if>
<if test="member.since">
<dl class="detailList">
<dt class="heading">Since:</dt>
<dd>{+ member.since +}</dd>
</dl>
</dl>
</if>
<if test="member.exceptions.length">
<dl class="detailList">
<dt class="heading">Throws:</dt>
<for each="item" in="member.exceptions">
<dt>
{+((item.type)?"<span class=\"light fixedFont\">{"+(new Link().toSymbol(item.type))+"}</span> " : "")+} <b>{+item.name+}</b>
</dt>
<dd>{+resolveLinks(item.desc)+}</dd>
</for>
</dl>
</if>
<if test="member.returns.length">
<dl class="detailList">
<dt class="heading">Returns:</dt>
<for each="item" in="member.returns">
<dd>{+((item.type)?"<span class=\"light fixedFont\">{"+(new Link().toSymbol(item.type))+"}</span> " : "")+}{+resolveLinks(item.desc)+}</dd>
</for>
</dl>
</if>
<if test="member.requires.length">
<dl class="detailList">
<dt class="heading">Requires:</dt>
<for each="item" in="member.requires">
<dd>{+ resolveLinks(item) +}</dd>
</for>
</dl>
</if>
<if test="member.see.length">
<dl class="detailList">
<dt class="heading">See:</dt>
<for each="item" in="member.see">
<dd>{+ new Link().toSymbol(item) +}</dd>
</for>
</dl>
</if>
<if test="!$member_last"><hr /></if>
</for>
</if>
<hr />
</div>
<!-- ============================== footer ================================= -->
<div class="fineprint" style="clear:both">
<if test="JSDOC.opt.D.copyright">&copy;{+JSDOC.opt.D.copyright+}<br /></if>
Documentation generated by <a href="http://www.jsdoctoolkit.org/" target="_blank">JsDoc Toolkit</a> {+JSDOC.VERSION+} on {+new Date()+}
</div>
</body>
</html>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset={+IO.encoding+}"" />
<title>JsDoc Reference - Index</title>
<meta name="generator" content="JsDoc Toolkit" />
<style type="text/css">
{+include("static/default.css")+}
</style>
</head>
<body>
{+include("static/header.html")+}
<div id="index">
{+publish.classesIndex+}
</div>
<div id="content">
<h1 class="classTitle">Class Index</h1>
<for each="thisClass" in="data">
<div>
<h2>{+(new Link().toSymbol(thisClass.alias))+}</h2>
{+resolveLinks(summarize(thisClass.classDesc))+}
</div>
<hr />
</for>
</div>
<div class="fineprint" style="clear:both">
<if test="JSDOC.opt.D.copyright">&copy;{+JSDOC.opt.D.copyright+}<br /></if>
Documentation generated by <a href="http://www.jsdoctoolkit.org/" target="_blankt">JsDoc Toolkit</a> {+JSDOC.VERSION+} on {+new Date()+}
</div>
</body>
</html>

184
vendor/jasmine/jsdoc-template/publish.js vendored Normal file
View File

@ -0,0 +1,184 @@
/** Called automatically by JsDoc Toolkit. */
function publish(symbolSet) {
publish.conf = { // trailing slash expected for dirs
ext: ".html",
outDir: JSDOC.opt.d || SYS.pwd+"../out/jsdoc/",
templatesDir: JSDOC.opt.t || SYS.pwd+"../templates/jsdoc/",
symbolsDir: "symbols/",
srcDir: "symbols/src/"
};
// is source output is suppressed, just display the links to the source file
if (JSDOC.opt.s && defined(Link) && Link.prototype._makeSrcLink) {
Link.prototype._makeSrcLink = function(srcFilePath) {
return "&lt;"+srcFilePath+"&gt;";
}
}
// create the folders and subfolders to hold the output
IO.mkPath((publish.conf.outDir+"symbols/src").split("/"));
// used to allow Link to check the details of things being linked to
Link.symbolSet = symbolSet;
// create the required templates
try {
var classTemplate = new JSDOC.JsPlate(publish.conf.templatesDir+"class.tmpl");
var classesTemplate = new JSDOC.JsPlate(publish.conf.templatesDir+"allclasses.tmpl");
}
catch(e) {
print("Couldn't create the required templates: "+e);
quit();
}
// some ustility filters
function hasNoParent($) {return ($.memberOf == "")}
function isaFile($) {return ($.is("FILE"))}
function isaClass($) {return ($.is("CONSTRUCTOR") || $.isNamespace)}
// get an array version of the symbolset, useful for filtering
var symbols = symbolSet.toArray();
// create the hilited source code files
var files = JSDOC.opt.srcFiles;
for (var i = 0, l = files.length; i < l; i++) {
var file = files[i];
var srcDir = publish.conf.outDir + "symbols/src/";
makeSrcFile(file, srcDir);
}
// get a list of all the classes in the symbolset
var classes = symbols.filter(isaClass).sort(makeSortby("alias"));
// create a class index, displayed in the left-hand column of every class page
Link.base = "../";
publish.classesIndex = classesTemplate.process(classes); // kept in memory
// create each of the class pages
for (var i = 0, l = classes.length; i < l; i++) {
var symbol = classes[i];
symbol.events = symbol.getEvents(); // 1 order matters
symbol.methods = symbol.getMethods(); // 2
var output = "";
output = classTemplate.process(symbol);
IO.saveFile(publish.conf.outDir+"symbols/", symbol.alias+publish.conf.ext, output);
}
// regenerate the index with different relative links, used in the index pages
Link.base = "";
publish.classesIndex = classesTemplate.process(classes);
// create the class index page
try {
var classesindexTemplate = new JSDOC.JsPlate(publish.conf.templatesDir+"index.tmpl");
}
catch(e) { print(e.message); quit(); }
var classesIndex = classesindexTemplate.process(classes);
IO.saveFile(publish.conf.outDir, "index"+publish.conf.ext, classesIndex);
classesindexTemplate = classesIndex = classes = null;
// create the file index page
try {
var fileindexTemplate = new JSDOC.JsPlate(publish.conf.templatesDir+"allfiles.tmpl");
}
catch(e) { print(e.message); quit(); }
var documentedFiles = symbols.filter(isaFile); // files that have file-level docs
var allFiles = []; // not all files have file-level docs, but we need to list every one
for (var i = 0; i < files.length; i++) {
allFiles.push(new JSDOC.Symbol(files[i], [], "FILE", new JSDOC.DocComment("/** */")));
}
for (var i = 0; i < documentedFiles.length; i++) {
var offset = files.indexOf(documentedFiles[i].alias);
allFiles[offset] = documentedFiles[i];
}
allFiles = allFiles.sort(makeSortby("name"));
// output the file index page
var filesIndex = fileindexTemplate.process(allFiles);
IO.saveFile(publish.conf.outDir, "files"+publish.conf.ext, filesIndex);
fileindexTemplate = filesIndex = files = null;
}
/** Just the first sentence (up to a full stop). Should not break on dotted variable names. */
function summarize(desc) {
if (typeof desc != "undefined")
return desc.match(/([\w\W]+?\.)[^a-z0-9_$]/i)? RegExp.$1 : desc;
}
/** Make a symbol sorter by some attribute. */
function makeSortby(attribute) {
return function(a, b) {
if (a[attribute] != undefined && b[attribute] != undefined) {
a = a[attribute].toLowerCase();
b = b[attribute].toLowerCase();
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
}
}
/** Pull in the contents of an external file at the given path. */
function include(path) {
var path = publish.conf.templatesDir+path;
return IO.readFile(path);
}
/** Turn a raw source file into a code-hilited page in the docs. */
function makeSrcFile(path, srcDir, name) {
if (JSDOC.opt.s) return;
if (!name) {
name = path.replace(/\.\.?[\\\/]/g, "").replace(/[\\\/]/g, "_");
name = name.replace(/\:/g, "_");
}
var src = {path: path, name:name, charset: IO.encoding, hilited: ""};
if (defined(JSDOC.PluginManager)) {
JSDOC.PluginManager.run("onPublishSrc", src);
}
if (src.hilited) {
IO.saveFile(srcDir, name+publish.conf.ext, src.hilited);
}
}
/** Build output for displaying function parameters. */
function makeSignature(params) {
if (!params) return "()";
var signature = "("
+
params.filter(
function($) {
return $.name.indexOf(".") == -1; // don't show config params in signature
}
).map(
function($) {
return $.name;
}
).join(", ")
+
")";
return signature;
}
/** Find symbol {@link ...} strings in text and turn into html links */
function resolveLinks(str, from) {
str = str.replace(/\{@link ([^} ]+) ?\}/gi,
function(match, symbolName) {
return new Link().toSymbol(symbolName);
}
);
return str;
}

View File

@ -0,0 +1,162 @@
/* default.css */
body
{
font: 12px "Lucida Grande", Tahoma, Arial, Helvetica, sans-serif;
width: 800px;
}
.header
{
clear: both;
background-color: #ccc;
padding: 8px;
}
h1
{
font-size: 150%;
font-weight: bold;
padding: 0;
margin: 1em 0 0 .3em;
}
hr
{
border: none 0;
border-top: 1px solid #7F8FB1;
height: 1px;
}
pre.code
{
display: block;
padding: 8px;
border: 1px dashed #ccc;
}
#index
{
margin-top: 24px;
float: left;
width: 160px;
position: absolute;
left: 8px;
background-color: #F3F3F3;
padding: 8px;
}
#content
{
margin-left: 190px;
width: 600px;
}
.classList
{
list-style-type: none;
padding: 0;
margin: 0 0 0 8px;
font-family: arial, sans-serif;
font-size: 1em;
overflow: auto;
}
.classList li
{
padding: 0;
margin: 0 0 8px 0;
}
.summaryTable { width: 100%; }
h1.classTitle
{
font-size:170%;
line-height:130%;
}
h2 { font-size: 110%; }
caption, div.sectionTitle
{
background-color: #7F8FB1;
color: #fff;
font-size:130%;
text-align: left;
padding: 2px 6px 2px 6px;
border: 1px #7F8FB1 solid;
}
div.sectionTitle { margin-bottom: 8px; }
.summaryTable thead { display: none; }
.summaryTable td
{
vertical-align: top;
padding: 4px;
border-bottom: 1px #7F8FB1 solid;
border-right: 1px #7F8FB1 solid;
}
/*col#summaryAttributes {}*/
.summaryTable td.attributes
{
border-left: 1px #7F8FB1 solid;
width: 140px;
text-align: right;
}
td.attributes, .fixedFont
{
line-height: 15px;
color: #002EBE;
font-family: "Courier New",Courier,monospace;
font-size: 13px;
}
.summaryTable td.nameDescription
{
text-align: left;
font-size: 13px;
line-height: 15px;
}
.summaryTable td.nameDescription, .description
{
line-height: 15px;
padding: 4px;
padding-left: 4px;
}
.summaryTable { margin-bottom: 8px; }
ul.inheritsList
{
list-style: square;
margin-left: 20px;
padding-left: 0;
}
.detailList {
margin-left: 20px;
line-height: 15px;
}
.detailList dt { margin-left: 20px; }
.detailList .heading
{
font-weight: bold;
padding-bottom: 6px;
margin-left: 0;
}
.light, td.attributes, .light a:link, .light a:visited
{
color: #777;
font-style: italic;
}
.fineprint
{
text-align: right;
font-size: 10px;
}

View File

@ -0,0 +1,2 @@
<div id="header">
</div>

View File

@ -0,0 +1,19 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Generated Javascript Documentation</title>
</head>
<frameset cols="20%,80%">
<frame src="allclasses-frame.html" name="packageFrame" />
<frame src="splash.html" name="classFrame" />
<noframes>
<body>
<p>
This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
</p>
</body>
</noframes>
</frameset>
</html>

View File

@ -0,0 +1,35 @@
<symbol alias="{+data.alias+}">
<name>{+data.name+}</name>
<memberOf>{+data.memberOf+}</memberOf>
<isStatic>{+data.isStatic+}</isStatic>
<isa>{+data.isa+}</isa>
<desc>{+data.desc+}</desc>
<classDesc>{+data.classDesc+}</classDesc>
<methods><for each="method" in="data.methods">
<method>
<name>{+method.name+}</name>
<memberOf>{+method.memberOf+}</memberOf>
<isStatic>{+method.isStatic+}</isStatic>
<desc>{+method.desc+}</desc>
<params><for each="param" in="method.params">
<param>
<type>{+param.type+}</type>
<name>{+param.name+}</name>
<desc>{+param.desc+}</desc>
<defaultValue>{+param.defaultValue+}</defaultValue>
</param></for>
</params>
</method></for>
</methods>
<properties><for each="property" in="data.properties">
<property>
<name>{+property.name+}</name>
<memberOf>{+property.memberOf+}</memberOf>
<isStatic>{+property.isStatic+}</isStatic>
<desc>{+property.desc+}</desc>
<type>{+property.type+}</type>
</property></for>
</properties>
</symbol>

5919
vendor/jasmine/jshint/jshint.js vendored Executable file

File diff suppressed because it is too large Load Diff

99
vendor/jasmine/jshint/run.js vendored Normal file
View File

@ -0,0 +1,99 @@
var fs = require("fs");
var util = require("util");
var path = require("path");
var JSHINT = require("./jshint").JSHINT;
// DWF TODO: Standardize this?
function isExcluded(fullPath) {
var fileName = path.basename(fullPath);
var excludeFiles = ["json2.js", "jshint.js", "publish.js", "node_suite.js", "jasmine.js", "jasmine-html.js"];
for (var i = 0; i < excludeFiles.length; i++) {
if (fileName == excludeFiles[i]) {
return true;
}
}
return false;
}
// DWF TODO: This function could/should be re-written
function allJasmineJsFiles(rootDir) {
var files = [];
fs.readdirSync(rootDir).filter(function(filename) {
var fullPath = rootDir + "/" + filename;
if (fs.statSync(fullPath).isDirectory() && !fullPath.match(/pages/)) {
var subDirFiles = allJasmineJsFiles(fullPath);
if (subDirFiles.length > 0) {
files = files.concat();
return true;
}
} else {
if (fullPath.match(/\.js$/) && !isExcluded(fullPath)) {
files.push(fullPath);
return true;
}
}
return false;
});
return files;
}
var jasmineJsFiles = allJasmineJsFiles(".");
jasmineJsFiles.reverse(); //cheap way to do the stuff in src stuff first
var jasmineJsHintConfig = {
forin:true, //while it's possible that we could be
//considering unwanted prototype methods, mostly
//we're doing this because the jsobjects are being
//used as maps.
loopfunc:true //we're fine with functions defined inside loops (setTimeout functions, etc)
};
var jasmineGlobals = {};
//jasmine.undefined is a jasmine-ism, let's let it go...
function removeJasmineUndefinedErrors(errors) {
var keepErrors = [];
for (var i = 0; i < errors.length; i++) {
if (!(errors[i] &&
errors[i].raw &&
errors[i].evidence &&
( errors[i].evidence.match(/jasmine\.undefined/) ||
errors[i].evidence.match(/diz be undefined yo/) )
)) {
keepErrors.push(errors[i]);
}
}
return keepErrors;
}
(function() {
var ansi = {
green: '\033[32m',
red: '\033[31m',
yellow: '\033[33m',
none: '\033[0m'
};
for (var i = 0; i < jasmineJsFiles.length; i++) {
var file = jasmineJsFiles[i];
JSHINT(fs.readFileSync(file, "utf8"), jasmineJsHintConfig);
var errors = JSHINT.data().errors || [];
errors = removeJasmineUndefinedErrors(errors);
if (errors.length >= 1) {
console.log(ansi.red + "Jasmine JSHint failure: " + ansi.none);
console.log(file);
console.log(errors);
process.exit(1);
}
}
console.log(ansi.green + "Jasmine JSHint PASSED." + ansi.none);
})();

36
vendor/jasmine/lib/jasmine-core.rb vendored Normal file
View File

@ -0,0 +1,36 @@
module Jasmine
module Core
class << self
def path
File.join(File.dirname(__FILE__), "jasmine-core")
end
def js_files
(["jasmine.js"] + Dir.glob(File.join(path, "*.js"))).map { |f| File.basename(f) }.uniq
end
SPEC_TYPES = ["core", "html", "node"]
def core_spec_files
spec_files("core")
end
def html_spec_files
spec_files("html")
end
def node_spec_files
spec_files("node")
end
def spec_files(type)
raise ArgumentError.new("Unrecognized spec type") unless SPEC_TYPES.include?(type)
(Dir.glob(File.join(path, "spec", type, "*.js"))).map { |f| File.join("spec", type, File.basename(f)) }.uniq
end
def css_files
Dir.glob(File.join(path, "*.css")).map { |f| File.basename(f) }
end
end
end
end

View File

@ -0,0 +1,54 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Jasmine Spec Runner</title>
<link rel="shortcut icon" type="image/png" href="lib/jasmine-1.1.0.rc1/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="lib/jasmine-1.1.0.rc1/jasmine.css">
<script type="text/javascript" src="lib/jasmine-1.1.0.rc1/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine-1.1.0.rc1/jasmine-html.js"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec/SpecHelper.js"></script>
<script type="text/javascript" src="spec/PlayerSpec.js"></script>
<!-- include source files here... -->
<script type="text/javascript" src="src/Player.js"></script>
<script type="text/javascript" src="src/Song.js"></script>
<script type="text/javascript">
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var trivialReporter = new jasmine.TrivialReporter();
jasmineEnv.addReporter(trivialReporter);
jasmineEnv.specFilter = function(spec) {
return trivialReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();
</script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,58 @@
describe("Player", function() {
var player;
var song;
beforeEach(function() {
player = new Player();
song = new Song();
});
it("should be able to play a Song", function() {
player.play(song);
expect(player.currentlyPlayingSong).toEqual(song);
//demonstrates use of custom matcher
expect(player).toBePlaying(song);
});
describe("when song has been paused", function() {
beforeEach(function() {
player.play(song);
player.pause();
});
it("should indicate that the song is currently paused", function() {
expect(player.isPlaying).toBeFalsy();
// demonstrates use of 'not' with a custom matcher
expect(player).not.toBePlaying(song);
});
it("should be possible to resume", function() {
player.resume();
expect(player.isPlaying).toBeTruthy();
expect(player.currentlyPlayingSong).toEqual(song);
});
});
// demonstrates use of spies to intercept and test method calls
it("tells the current song if the user has made it a favorite", function() {
spyOn(song, 'persistFavoriteStatus');
player.play(song);
player.makeFavorite();
expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
});
//demonstrates use of expected exceptions
describe("#resume", function() {
it("should throw an exception if song is already playing", function() {
player.play(song);
expect(function() {
player.resume();
}).toThrow("song is already playing");
});
});
});

View File

@ -0,0 +1,9 @@
beforeEach(function() {
this.addMatchers({
toBePlaying: function(expectedSong) {
var player = this.actual;
return player.currentlyPlayingSong === expectedSong &&
player.isPlaying;
}
});
});

View File

@ -0,0 +1,22 @@
function Player() {
}
Player.prototype.play = function(song) {
this.currentlyPlayingSong = song;
this.isPlaying = true;
};
Player.prototype.pause = function() {
this.isPlaying = false;
};
Player.prototype.resume = function() {
if (this.isPlaying) {
throw new Error("song is already playing");
}
this.isPlaying = true;
};
Player.prototype.makeFavorite = function() {
this.currentlyPlayingSong.persistFavoriteStatus(true);
};

View File

@ -0,0 +1,7 @@
function Song() {
}
Song.prototype.persistFavoriteStatus = function(value) {
// something complicated
throw new Error("not yet implemented");
};

View File

@ -0,0 +1,681 @@
jasmine.HtmlReporterHelpers = {};
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) {
el.appendChild(child);
}
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
var results = child.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
return status;
};
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
var parentDiv = this.dom.summary;
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
var parent = child[parentSuite];
if (parent) {
if (typeof this.views.suites[parent.id] == 'undefined') {
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
}
parentDiv = this.views.suites[parent.id].element;
}
parentDiv.appendChild(childElement);
};
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
for(var fn in jasmine.HtmlReporterHelpers) {
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
}
};
jasmine.HtmlReporter = function(_doc) {
var self = this;
var doc = _doc || window.document;
var reporterView;
var dom = {};
// Jasmine Reporter Public Interface
self.logRunningSpecs = false;
self.reportRunnerStarting = function(runner) {
var specs = runner.specs() || [];
if (specs.length == 0) {
return;
}
createReporterDom(runner.env.versionString());
doc.body.appendChild(dom.reporter);
setExceptionHandling();
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
reporterView.addSpecs(specs, self.specFilter);
};
self.reportRunnerResults = function(runner) {
reporterView && reporterView.complete();
};
self.reportSuiteResults = function(suite) {
reporterView.suiteComplete(suite);
};
self.reportSpecStarting = function(spec) {
if (self.logRunningSpecs) {
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
self.reportSpecResults = function(spec) {
reporterView.specComplete(spec);
};
self.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
self.specFilter = function(spec) {
if (!focusedSpecName()) {
return true;
}
return spec.getFullName().indexOf(focusedSpecName()) === 0;
};
return self;
function focusedSpecName() {
var specName;
(function memoizeFocusedSpec() {
if (specName) {
return;
}
var paramMap = [];
var params = jasmine.HtmlReporter.parameters(doc);
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
specName = paramMap.spec;
})();
return specName;
}
function createReporterDom(version) {
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
dom.banner = self.createDom('div', { className: 'banner' },
self.createDom('span', { className: 'title' }, "Jasmine "),
self.createDom('span', { className: 'version' }, version)),
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
dom.alert = self.createDom('div', {className: 'alert'},
self.createDom('span', { className: 'exceptions' },
self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'),
self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),
dom.results = self.createDom('div', {className: 'results'},
dom.summary = self.createDom('div', { className: 'summary' }),
dom.details = self.createDom('div', { id: 'details' }))
);
}
function noTryCatch() {
return window.location.search.match(/catch=false/);
}
function searchWithCatch() {
var params = jasmine.HtmlReporter.parameters(window.document);
var removed = false;
var i = 0;
while (!removed && i < params.length) {
if (params[i].match(/catch=/)) {
params.splice(i, 1);
removed = true;
}
i++;
}
if (jasmine.CATCH_EXCEPTIONS) {
params.push("catch=false");
}
return params.join("&");
}
function setExceptionHandling() {
var chxCatch = document.getElementById('no_try_catch');
if (noTryCatch()) {
chxCatch.setAttribute('checked', true);
jasmine.CATCH_EXCEPTIONS = false;
}
chxCatch.onclick = function() {
window.location.search = searchWithCatch();
};
}
};
jasmine.HtmlReporter.parameters = function(doc) {
var paramStr = doc.location.search.substring(1);
var params = [];
if (paramStr.length > 0) {
params = paramStr.split('&');
}
return params;
}
jasmine.HtmlReporter.sectionLink = function(sectionName) {
var link = '?';
var params = [];
if (sectionName) {
params.push('spec=' + encodeURIComponent(sectionName));
}
if (!jasmine.CATCH_EXCEPTIONS) {
params.push("catch=false");
}
if (params.length > 0) {
link += params.join("&");
}
return link;
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
jasmine.HtmlReporter.ReporterView = function(dom) {
this.startedAt = new Date();
this.runningSpecCount = 0;
this.completeSpecCount = 0;
this.passedCount = 0;
this.failedCount = 0;
this.skippedCount = 0;
this.createResultsMenu = function() {
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
' | ',
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
this.summaryMenuItem.onclick = function() {
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
};
this.detailsMenuItem.onclick = function() {
showDetails();
};
};
this.addSpecs = function(specs, specFilter) {
this.totalSpecCount = specs.length;
this.views = {
specs: {},
suites: {}
};
for (var i = 0; i < specs.length; i++) {
var spec = specs[i];
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
if (specFilter(spec)) {
this.runningSpecCount++;
}
}
};
this.specComplete = function(spec) {
this.completeSpecCount++;
if (isUndefined(this.views.specs[spec.id])) {
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
}
var specView = this.views.specs[spec.id];
switch (specView.status()) {
case 'passed':
this.passedCount++;
break;
case 'failed':
this.failedCount++;
break;
case 'skipped':
this.skippedCount++;
break;
}
specView.refresh();
this.refresh();
};
this.suiteComplete = function(suite) {
var suiteView = this.views.suites[suite.id];
if (isUndefined(suiteView)) {
return;
}
suiteView.refresh();
};
this.refresh = function() {
if (isUndefined(this.resultsMenu)) {
this.createResultsMenu();
}
// currently running UI
if (isUndefined(this.runningAlert)) {
this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
dom.alert.appendChild(this.runningAlert);
}
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
// skipped specs UI
if (isUndefined(this.skippedAlert)) {
this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
}
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.skippedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.skippedAlert);
}
// passing specs UI
if (isUndefined(this.passedAlert)) {
this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
}
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
// failing specs UI
if (isUndefined(this.failedAlert)) {
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
}
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
if (this.failedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.failedAlert);
dom.alert.appendChild(this.resultsMenu);
}
// summary info
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
};
this.complete = function() {
dom.alert.removeChild(this.runningAlert);
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.failedCount === 0) {
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
} else {
showDetails();
}
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
};
return this;
function showDetails() {
if (dom.reporter.className.search(/showDetails/) === -1) {
dom.reporter.className += " showDetails";
}
}
function isUndefined(obj) {
return typeof obj === 'undefined';
}
function isDefined(obj) {
return !isUndefined(obj);
}
function specPluralizedFor(count) {
var str = count + " spec";
if (count > 1) {
str += "s"
}
return str;
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
this.spec = spec;
this.dom = dom;
this.views = views;
this.symbol = this.createDom('li', { className: 'pending' });
this.dom.symbolSummary.appendChild(this.symbol);
this.summary = this.createDom('div', { className: 'specSummary' },
this.createDom('a', {
className: 'description',
href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.description)
);
this.detail = this.createDom('div', { className: 'specDetail' },
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.getFullName())
);
};
jasmine.HtmlReporter.SpecView.prototype.status = function() {
return this.getSpecStatus(this.spec);
};
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
this.symbol.className = this.status();
switch (this.status()) {
case 'skipped':
break;
case 'passed':
this.appendSummaryToSuiteDiv();
break;
case 'failed':
this.appendSummaryToSuiteDiv();
this.appendFailureDetail();
break;
}
};
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
this.summary.className += ' ' + this.status();
this.appendToSummary(this.spec, this.summary);
};
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
this.detail.className += ' ' + this.status();
var resultItems = this.spec.results().getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
this.detail.appendChild(messagesDiv);
this.dom.details.appendChild(this.detail);
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
this.suite = suite;
this.dom = dom;
this.views = views;
this.element = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
);
this.appendToSummary(this.suite, this.element);
};
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
return this.getSpecStatus(this.suite);
};
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
this.element.className += " " + this.status();
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
/* @deprecated Use jasmine.HtmlReporter instead
*/
jasmine.TrivialReporter = function(doc) {
this.document = doc || document;
this.suiteDivs = {};
this.logRunningSpecs = false;
};
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) { el.appendChild(child); }
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
var showPassed, showSkipped;
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
this.createDom('div', { className: 'banner' },
this.createDom('div', { className: 'logo' },
this.createDom('span', { className: 'title' }, "Jasmine"),
this.createDom('span', { className: 'version' }, runner.env.versionString())),
this.createDom('div', { className: 'options' },
"Show ",
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
)
),
this.runnerDiv = this.createDom('div', { className: 'runner running' },
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
);
this.document.body.appendChild(this.outerDiv);
var suites = runner.suites();
for (var i = 0; i < suites.length; i++) {
var suite = suites[i];
var suiteDiv = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
this.suiteDivs[suite.id] = suiteDiv;
var parentDiv = this.outerDiv;
if (suite.parentSuite) {
parentDiv = this.suiteDivs[suite.parentSuite.id];
}
parentDiv.appendChild(suiteDiv);
}
this.startedAt = new Date();
var self = this;
showPassed.onclick = function(evt) {
if (showPassed.checked) {
self.outerDiv.className += ' show-passed';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
}
};
showSkipped.onclick = function(evt) {
if (showSkipped.checked) {
self.outerDiv.className += ' show-skipped';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
}
};
};
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
var results = runner.results();
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
this.runnerDiv.setAttribute("class", className);
//do it twice for IE
this.runnerDiv.setAttribute("className", className);
var specs = runner.specs();
var specCount = 0;
for (var i = 0; i < specs.length; i++) {
if (this.specFilter(specs[i])) {
specCount++;
}
}
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
};
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
var results = suite.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.totalCount === 0) { // todo: change this to check results.skipped
status = 'skipped';
}
this.suiteDivs[suite.id].className += " " + status;
};
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
if (this.logRunningSpecs) {
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
var results = spec.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
var specDiv = this.createDom('div', { className: 'spec ' + status },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(spec.getFullName()),
title: spec.getFullName()
}, spec.description));
var resultItems = results.getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
specDiv.appendChild(messagesDiv);
}
this.suiteDivs[spec.suite.id].appendChild(specDiv);
};
jasmine.TrivialReporter.prototype.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
jasmine.TrivialReporter.prototype.getLocation = function() {
return this.document.location;
};
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
var paramMap = {};
var params = this.getLocation().search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
if (!paramMap.spec) {
return true;
}
return spec.getFullName().indexOf(paramMap.spec) === 0;
};

View File

@ -0,0 +1,82 @@
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
#HTMLReporter a { text-decoration: none; }
#HTMLReporter a:hover { text-decoration: underline; }
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
#HTMLReporter .version { color: #aaaaaa; }
#HTMLReporter .banner { margin-top: 14px; }
#HTMLReporter .duration { color: #aaaaaa; float: right; }
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
#HTMLReporter .runningAlert { background-color: #666666; }
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
#HTMLReporter .passingAlert { background-color: #a6b779; }
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
#HTMLReporter .failingAlert { background-color: #cf867e; }
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
#HTMLReporter .results { margin-top: 14px; }
#HTMLReporter #details { display: none; }
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter.showDetails .summary { display: none; }
#HTMLReporter.showDetails #details { display: block; }
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter .summary { margin-top: 14px; }
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
#HTMLReporter .description + .suite { margin-top: 0; }
#HTMLReporter .suite { margin-top: 14px; }
#HTMLReporter .suite a { color: #333333; }
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
#HTMLReporter .resultMessage span.result { display: block; }
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
#TrivialReporter .runner.running { background-color: yellow; }
#TrivialReporter .options { text-align: right; font-size: .8em; }
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
#TrivialReporter .suite .suite { margin: 5px; }
#TrivialReporter .suite.passed { background-color: #dfd; }
#TrivialReporter .suite.failed { background-color: #fdd; }
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
#TrivialReporter .spec.skipped { background-color: #bbb; }
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
#TrivialReporter .passed { background-color: #cfc; display: none; }
#TrivialReporter .failed { background-color: #fbb; }
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
#TrivialReporter .resultMessage .mismatch { color: black; }
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }

2538
vendor/jasmine/lib/jasmine-core/jasmine.js vendored Normal file

File diff suppressed because it is too large Load Diff

478
vendor/jasmine/lib/jasmine-core/json2.js vendored Normal file
View File

@ -0,0 +1,478 @@
/*
http://www.JSON.org/json2.js
2009-08-17
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See http://www.JSON.org/js.html
This file creates a global JSON object containing two methods: stringify
and parse.
JSON.stringify(value, replacer, space)
value any JavaScript value, usually an object or array.
replacer an optional parameter that determines how object
values are stringified for objects. It can be a
function or an array of strings.
space an optional parameter that specifies the indentation
of nested structures. If it is omitted, the text will
be packed without extra whitespace. If it is a number,
it will specify the number of spaces to indent at each
level. If it is a string (such as '\t' or '&nbsp;'),
it contains the characters used to indent at each level.
This method produces a JSON text from a JavaScript value.
When an object value is found, if the object contains a toJSON
method, its toJSON method will be called and the result will be
stringified. A toJSON method does not serialize: it returns the
value represented by the name/value pair that should be serialized,
or undefined if nothing should be serialized. The toJSON method
will be passed the key associated with the value, and this will be
bound to the value
For example, this would serialize Dates as ISO strings.
Date.prototype.toJSON = function (key) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
You can provide an optional replacer method. It will be passed the
key and value of each member, with this bound to the containing
object. The value that is returned from your method will be
serialized. If your method returns undefined, then the member will
be excluded from the serialization.
If the replacer parameter is an array of strings, then it will be
used to select the members to be serialized. It filters the results
such that only members with keys listed in the replacer array are
stringified.
Values that do not have JSON representations, such as undefined or
functions, will not be serialized. Such values in objects will be
dropped; in arrays they will be replaced with null. You can use
a replacer function to replace those with JSON values.
JSON.stringify(undefined) returns undefined.
The optional space parameter produces a stringification of the
value that is filled with line breaks and indentation to make it
easier to read.
If the space parameter is a non-empty string, then that string will
be used for indentation. If the space parameter is a number, then
the indentation will be that many spaces.
Example:
text = JSON.stringify(['e', {pluribus: 'unum'}]);
// text is '["e",{"pluribus":"unum"}]'
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
text = JSON.stringify([new Date()], function (key, value) {
return this[key] instanceof Date ?
'Date(' + this[key] + ')' : value;
});
// text is '["Date(---current time---)"]'
JSON.parse(text, reviver)
This method parses a JSON text to produce an object or array.
It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
Example:
// Parse the text. Values that look like ISO date strings will
// be converted to Date objects.
myData = JSON.parse(text, function (key, value) {
var a;
if (typeof value === 'string') {
a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+a[5], +a[6]));
}
}
return value;
});
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
var d;
if (typeof value === 'string' &&
value.slice(0, 5) === 'Date(' &&
value.slice(-1) === ')') {
d = new Date(value.slice(5, -1));
if (d) {
return d;
}
}
return value;
});
This is a reference implementation. You are free to copy, modify, or
redistribute.
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
*/
/*jslint evil: true */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf
*/
"use strict";
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
if (!this.JSON) {
this.JSON = {};
}
(function () {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function (key) {
return isFinite(this.valueOf()) ?
this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z' : null;
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function (key) {
return this.valueOf();
};
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ?
'"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' :
'"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0 ? '[]' :
gap ? '[\n' + gap +
partial.join(',\n' + gap) + '\n' +
mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
k = rep[i];
if (typeof k === 'string') {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' :
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
mind + '}' : '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
};
}
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' ?
walk({'': j}, '') : j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
};
}
}());

View File

@ -0,0 +1,451 @@
describe("ConsoleReporter", function() {
//keep these literal. otherwise the test loses value as a test.
function green(str) {
return '\033[32m' + str + '\033[0m';
}
function red(str) {
return '\033[31m' + str + '\033[0m';
}
function yellow(str) {
return '\033[33m' + str + '\033[0m';
}
function prefixGreen(str) {
return '\033[32m' + str;
}
function prefixRed(str) {
return '\033[31m' + str;
}
var newline = "\n";
var passingSpec = {
results: function() {
return {
passed: function() {
return true;
}
};
}
},
failingSpec = {
results: function() {
return {
passed: function() {
return false;
}
};
}
},
skippedSpec = {
results: function() {
return {skipped: true};
}
},
passingRun = {
specs: function() {
return [null, null, null];
},
results: function() {
return {failedCount: 0, items_: [null, null, null]};
}
},
failingRun = {
specs: function() {
return [null, null, null];
},
results: function() {
return {
failedCount: 7, items_: [null, null, null]};
}
};
function repeatedlyInvoke(f, times) {
for (var i = 0; i < times; i++) f(times + 1);
}
function repeat(thing, times) {
var arr = [];
for (var i = 0; i < times; i++) arr.push(thing);
return arr;
}
function simulateRun(reporter, specResults, suiteResults, finalRunner, startTime, endTime) {
reporter.reportRunnerStarting();
for (var i = 0; i < specResults.length; i++) {
reporter.reportSpecResults(specResults[i]);
}
for (i = 0; i < suiteResults.length; i++) {
reporter.reportSuiteResults(suiteResults[i]);
}
reporter.runnerStartTime = startTime;
reporter.now = function() {
return endTime;
};
reporter.reportRunnerResults(finalRunner);
}
var reporter, out, done;
beforeEach(function() {
out = (function() {
var output = "";
return {
print:function(str) {
output += str;
},
getOutput:function() {
return output;
},
clear: function() {
output = "";
}
};
})();
done = false;
reporter = new jasmine.ConsoleReporter(out.print, function(runner) {
done = true
});
});
describe('Integration', function() {
it("prints the proper output under a pass scenario - small numbers.", function() {
simulateRun(reporter,
repeat(passingSpec, 3),
[],
{
specs: function() {
return [null, null, null];
},
results:function() {
return {
items_: [null, null, null],
totalCount: 7,
failedCount: 0
};
}
},
1000,
1777
);
var output = out.getOutput();
expect(output).toMatch(/^Started/);
expect(output).toMatch(/\.\.\./);
expect(output).toMatch(/3 specs, 0 failures/);
});
it("prints the proper output under a pass scenario. large numbers.", function() {
simulateRun(reporter,
repeat(passingSpec, 57),
[],
{
specs: function() {
return [null, null, null];
},
results:function() {
return {
items_: [null, null, null],
totalCount: 7,
failedCount: 0
};
}
},
1000,
1777);
var output = out.getOutput();
expect(output).toMatch(/^Started/);
expect(output).toMatch(/\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\./);
expect(output).toMatch(/3 specs, 0 failures/);
});
it("prints the proper output under a failure scenario.", function() {
simulateRun(reporter,
[failingSpec, passingSpec, failingSpec],
[
{description:"The oven",
results:function() {
return {
items_:[
{failedCount:2,
description:"heats up",
items_:[
{trace:{stack:"stack trace one\n second line"}},
{trace:{stack:"stack trace two"}}
]}
]
};
}},
{description:"The washing machine",
results:function() {
return {
items_:[
{failedCount:2,
description:"washes clothes",
items_:[
{trace:{stack:"stack trace one"}}
]}
]
};
}}
],
{
specs: function() {
return [null, null, null];
},
results:function() {
return {
items_: [null, null, null],
totalCount: 7,
failedCount: 2
};
}
},
1000,
1777);
var output = out.getOutput();
expect(output).toMatch(/^Started/);
expect(output).toMatch(/F\.F/);
expect(output).toMatch(/The oven heats up\n stack trace one\n second line\n stack trace two/);
expect(output).toMatch(/The washing machine washes clothes\n stack trace one/);
expect(output).toMatch(/3 specs, 2 failures/);
});
});
describe('When a Jasmine environment executes', function() {
beforeEach(function() {
reporter.reportRunnerStarting();
});
it("should print 'Started' to the console", function() {
expect(out.getOutput()).toEqual("Started" + newline);
});
describe('when a spec reports', function() {
beforeEach(function() {
out.clear();
});
it("prints a green dot if the spec passes", function() {
reporter.reportSpecResults(passingSpec);
expect(out.getOutput()).toMatch(/\./);
});
it("prints a red dot if the spec fails", function() {
reporter.reportSpecResults(failingSpec);
expect(out.getOutput()).toMatch(/F/);
});
it("prints a yellow star if the spec was skipped", function() {
reporter.reportSpecResults(skippedSpec);
expect(out.getOutput()).toMatch(/\*/);
});
});
describe('when a suite reports', function() {
var emptyResults;
beforeEach(function() {
emptyResults = function() {
return {
items_:[]
};
};
});
it("remembers suite results", function() {
reporter.reportSuiteResults({description: "Oven", results: emptyResults});
reporter.reportSuiteResults({description: "Mixer", results: emptyResults});
expect(reporter.suiteResults[0].description).toEqual('Oven');
expect(reporter.suiteResults[1].description).toEqual('Mixer');
});
it("creates a description out of the current suite and any parent suites", function() {
var grandparentSuite = {
description: "My house",
results: emptyResults
};
var parentSuite = {
description: "kitchen",
parentSuite: grandparentSuite,
results: emptyResults
};
reporter.reportSuiteResults({ description: "oven", parentSuite: parentSuite, results: emptyResults });
expect(reporter.suiteResults[0].description).toEqual("My house kitchen oven");
});
it("gathers failing spec results from the suite - the spec must have a description.", function() {
reporter.reportSuiteResults({description:"Oven",
results: function() {
return {
items_:[
{ failedCount: 0, description: "specOne" },
{ failedCount: 99, description: "specTwo" },
{ failedCount: 0, description: "specThree" },
{ failedCount: 88, description: "specFour" },
{ failedCount: 3 }
]
};
}});
expect(reporter.suiteResults[0].failedSpecResults).
toEqual([
{ failedCount: 99, description: "specTwo" },
{ failedCount: 88, description: "specFour" }
]);
});
});
describe('and finishes', function() {
describe('when reporting spec failure information', function() {
it("prints suite and spec descriptions together as a sentence", function() {
reporter.suiteResults = [
{description:"The oven", failedSpecResults:[
{description:"heats up", items_:[]},
{description:"cleans itself", items_:[]}
]},
{description:"The mixer", failedSpecResults:[
{description:"blends things together", items_:[]}
]}
];
reporter.reportRunnerResults(failingRun);
expect(out.getOutput()).toContain("The oven heats up");
expect(out.getOutput()).toContain("The oven cleans itself");
expect(out.getOutput()).toContain("The mixer blends things together");
});
it("prints stack trace of spec failure", function() {
reporter.suiteResults = [
{description:"The oven", failedSpecResults:[
{description:"heats up",
items_:[
{trace:{stack:"stack trace one"}},
{trace:{stack:"stack trace two"}}
]}
]}
];
reporter.reportRunnerResults(failingRun);
expect(out.getOutput()).toContain("The oven heats up");
expect(out.getOutput()).toContain("stack trace one");
expect(out.getOutput()).toContain("stack trace two");
});
});
describe('when reporting the execution time', function() {
it("prints the full finished message", function() {
reporter.now = function() {
return 1000;
};
reporter.reportRunnerStarting();
reporter.now = function() {
return 1777;
};
reporter.reportRunnerResults(failingRun);
expect(out.getOutput()).toContain("Finished in 0.777 seconds");
});
it("prints round time numbers correctly", function() {
function run(startTime, endTime) {
out.clear();
reporter.runnerStartTime = startTime;
reporter.now = function() {
return endTime;
};
reporter.reportRunnerResults(passingRun);
}
run(1000, 11000);
expect(out.getOutput()).toContain("10 seconds");
run(1000, 2000);
expect(out.getOutput()).toContain("1 seconds");
run(1000, 1100);
expect(out.getOutput()).toContain("0.1 seconds");
run(1000, 1010);
expect(out.getOutput()).toContain("0.01 seconds");
run(1000, 1001);
expect(out.getOutput()).toContain("0.001 seconds");
});
});
describe("when reporting the results summary", function() {
it("prints statistics in green if there were no failures", function() {
reporter.reportRunnerResults({
specs: function() {
return [null, null, null];
},
results:function() {
return {items_: [null, null, null], totalCount: 7, failedCount: 0};
}
});
expect(out.getOutput()).
toContain("3 specs, 0 failures");
});
it("prints statistics in red if there was a failure", function() {
reporter.reportRunnerResults({
specs: function() {
return [null, null, null];
},
results:function() {
return {items_: [null, null, null], totalCount: 7, failedCount: 3};
}
});
expect(out.getOutput()).
toContain("3 specs, 3 failures");
});
it("handles pluralization with 1's ones appropriately", function() {
reporter.reportRunnerResults({
specs: function() {
return [null];
},
results:function() {
return {items_: [null], totalCount: 1, failedCount: 1};
}
});
expect(out.getOutput()).
toContain("1 spec, 1 failure");
});
});
describe("done callback", function() {
it("calls back when done", function() {
expect(done).toBeFalsy();
reporter.reportRunnerResults({
specs: function() {
return [null, null, null];
},
results:function() {
return {items_: [null, null, null], totalCount: 7, failedCount: 0};
}
});
expect(done).toBeTruthy();
});
});
});
});
});

View File

@ -0,0 +1,27 @@
describe("base.js", function() {
describe("jasmine.MessageResult", function() {
it("#toString should pretty-print and concatenate each part of the message", function() {
var values = ["log", "message", 123, {key: "value"}, "FTW!"];
var messageResult = new jasmine.MessageResult(values);
expect(messageResult.toString()).toEqual("log message 123 { key : 'value' } FTW!");
});
});
describe("jasmine.log", function() {
it("should accept n arguments", function() {
spyOn(jasmine.getEnv().currentSpec, 'log');
jasmine.log(1, 2, 3);
expect(jasmine.getEnv().currentSpec.log).toHaveBeenCalledWith(1, 2, 3);
});
});
describe("jasmine.getGlobal", function() {
it("should return the global object", function() {
var globalObject = (function() {
return this;
})();
expect(jasmine.getGlobal()).toBe(globalObject);
});
});
});

View File

@ -0,0 +1,97 @@
describe("Custom Matchers", function() {
var env;
var fakeTimer;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
});
it("should be easy to add more matchers local to a spec, suite, etc.", function() {
var spec1, spec2, spec1Matcher, spec2Matcher;
var suite = env.describe('some suite', function() {
env.beforeEach(function() {
this.addMatchers({
matcherForSuite: function(expected) {
this.message = "matcherForSuite: actual: " + this.actual + "; expected: " + expected;
return true;
}
});
});
spec1 = env.it('spec with an expectation').runs(function () {
this.addMatchers({
matcherForSpec: function(expected) {
this.message = "matcherForSpec: actual: " + this.actual + "; expected: " + expected;
return true;
}
});
spec1Matcher = this.expect("xxx");
});
spec2 = env.it('spec with failing expectation').runs(function () {
spec2Matcher = this.expect("yyy");
});
});
suite.execute();
spec1Matcher.matcherForSuite("expected");
expect(spec1Matcher.message).toEqual("matcherForSuite: actual: xxx; expected: expected");
spec1Matcher.matcherForSpec("expected");
expect(spec1Matcher.message).toEqual("matcherForSpec: actual: xxx; expected: expected");
spec2Matcher.matcherForSuite("expected");
expect(spec2Matcher.message).toEqual("matcherForSuite: actual: yyy; expected: expected");
expect(spec2Matcher.matcherForSpec).toBe(jasmine.undefined);
});
it("should generate messages with the same rules as for regular matchers when this.report() is not called", function() {
var spec;
var suite = env.describe('some suite', function() {
spec = env.it('spec with an expectation').runs(function () {
this.addMatchers({
toBeTrue: function() {
return this.actual === true;
}
});
this.expect(true).toBeTrue();
this.expect(false).toBeTrue();
});
});
suite.execute();
var passResult = new jasmine.ExpectationResult({passed: true, matcherName: 'toBeTrue',
actual: true, expected: jasmine.undefined, message: "Passed." });
var failResult = new jasmine.ExpectationResult({passed: false, matcherName: 'toBeTrue',
actual: false, expected: jasmine.undefined, message: "Expected false to be true." });
failResult.trace = jasmine.any(Object);
expect(spec.results().getItems()).toEqual([passResult, failResult]);
});
it("should pass args", function() {
var matcherCallArgs = [];
var spec;
var suite = env.describe('some suite', function() {
spec = env.it('spec with an expectation').runs(function () {
this.addMatchers({
toBeTrue: function() {
matcherCallArgs.push(jasmine.util.argsToArray(arguments));
return this.actual === true;
}
});
this.expect(true).toBeTrue();
this.expect(false).toBeTrue('arg');
this.expect(true).toBeTrue('arg1', 'arg2');
});
});
suite.execute();
var results = spec.results().getItems();
expect(results[0].expected).toEqual(jasmine.undefined);
expect(results[1].expected).toEqual('arg');
expect(results[2].expected).toEqual(['arg1', 'arg2']);
expect(matcherCallArgs).toEqual([[], ['arg'], ['arg1', 'arg2']]);
});
});

View File

@ -0,0 +1,159 @@
describe("jasmine.Env", function() {
var env;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
});
describe('ids', function () {
it('nextSpecId should return consecutive integers, starting at 0', function () {
expect(env.nextSpecId()).toEqual(0);
expect(env.nextSpecId()).toEqual(1);
expect(env.nextSpecId()).toEqual(2);
});
});
describe("reporting", function() {
var fakeReporter;
beforeEach(function() {
fakeReporter = jasmine.createSpyObj("fakeReporter", ["log"]);
});
describe('version', function () {
var oldVersion;
beforeEach(function () {
oldVersion = jasmine.version_;
});
afterEach(function () {
jasmine.version_ = oldVersion;
});
it('should raise an error if version is not set', function () {
jasmine.version_ = null;
var exception;
try {
env.version();
}
catch (e) {
exception = e;
}
expect(exception.message).toEqual('Version not set');
});
it("version should return the current version as an int", function() {
jasmine.version_ = {
"major": 1,
"minor": 9,
"build": 7,
"revision": 8
};
expect(env.version()).toEqual({
"major": 1,
"minor": 9,
"build": 7,
"revision": 8
});
});
describe("versionString", function() {
it("should return a stringified version number", function() {
jasmine.version_ = {
"major": 1,
"minor": 9,
"build": 7,
"release_candidate": "1",
"revision": 8
};
expect(env.versionString()).toEqual("1.9.7.rc1 revision 8");
});
it("should return a nice string when version is unknown", function() {
jasmine.version_ = null;
expect(env.versionString()).toEqual("version unknown");
});
});
});
it("should allow reporters to be registered", function() {
env.addReporter(fakeReporter);
env.reporter.log("message");
expect(fakeReporter.log).toHaveBeenCalledWith("message");
});
});
describe("equality testing", function() {
describe("with custom equality testers", function() {
var aObj, bObj, isEqual;
beforeEach(function() {
env.addEqualityTester(function(a, b) {
aObj = a;
bObj = b;
return isEqual;
});
});
it("should call the custom equality tester with two objects for comparison", function() {
env.equals_("1", "2");
expect(aObj).toEqual("1");
expect(bObj).toEqual("2");
});
describe("when the custom equality tester returns false", function() {
beforeEach(function() {
isEqual = false;
});
it("should give custom equality testers precedence", function() {
expect(env.equals_('abc', 'abc')).toBeFalsy();
var o = {};
expect(env.equals_(o, o)).toBeFalsy();
});
});
describe("when the custom equality tester returns true", function() {
beforeEach(function() {
isEqual = true;
});
it("should give custom equality testers precedence", function() {
expect(env.equals_('abc', 'def')).toBeTruthy();
expect(env.equals_(true, false)).toBeTruthy();
});
});
describe("when the custom equality tester returns undefined", function() {
beforeEach(function() {
isEqual = jasmine.undefined;
});
it("should use normal equality rules", function() {
expect(env.equals_('abc', 'abc')).toBeTruthy();
expect(env.equals_('abc', 'def')).toBeFalsy();
});
describe("even if there are several", function() {
beforeEach(function() {
env.addEqualityTester(function(a, b) { return jasmine.undefined; });
env.addEqualityTester(function(a, b) { return jasmine.undefined; });
});
it("should use normal equality rules", function() {
expect(env.equals_('abc', 'abc')).toBeTruthy();
expect(env.equals_('abc', 'def')).toBeFalsy();
});
});
});
it("should evaluate custom equality testers in the order they are declared", function() {
isEqual = false;
env.addEqualityTester(function(a, b) { return true; });
expect(env.equals_('abc', 'abc')).toBeFalsy();
});
});
});
});

View File

@ -0,0 +1,175 @@
describe('Exceptions:', function() {
var env;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
});
it('jasmine.formatException formats Firefox exception messages as expected', function() {
var sampleFirefoxException = {
fileName: 'foo.js',
line: '1978',
message: 'you got your foo in my bar',
name: 'A Classic Mistake'
};
var expected = 'A Classic Mistake: you got your foo in my bar in foo.js (line 1978)';
expect(jasmine.util.formatException(sampleFirefoxException)).toEqual(expected);
});
it('jasmine.formatException formats Webkit exception messages as expected', function() {
var sampleWebkitException = {
sourceURL: 'foo.js',
lineNumber: '1978',
message: 'you got your foo in my bar',
name: 'A Classic Mistake'
};
var expected = 'A Classic Mistake: you got your foo in my bar in foo.js (line 1978)';
expect(jasmine.util.formatException(sampleWebkitException)).toEqual(expected);
});
describe('with break on exception', function() {
it('should not catch the exception', function() {
var suite = env.describe('suite for break on exceptions', function() {
env.it('should break when an exception is thrown', function() {
throw new Error('I should hit a breakpoint!');
});
});
var runner = env.currentRunner();
var dont_change = 'I will never change!';
var oldCatch = jasmine.CATCH_EXCEPTIONS;
jasmine.CATCH_EXCEPTIONS = false;
try {
suite.execute();
dont_change = 'oops I changed';
}
catch (e) {}
finally {
jasmine.CATCH_EXCEPTIONS = oldCatch;
}
expect(dont_change).toEqual('I will never change!');
});
});
describe("with catch on exception", function() {
it('should handle exceptions thrown, but continue', function() {
var fakeTimer = new jasmine.FakeTimer();
env.setTimeout = fakeTimer.setTimeout;
env.clearTimeout = fakeTimer.clearTimeout;
env.setInterval = fakeTimer.setInterval;
env.clearInterval = fakeTimer.clearInterval;
//we run two exception tests to make sure we continue after throwing an exception
var suite = env.describe('Suite for handles exceptions', function () {
env.it('should be a test that fails because it throws an exception', function() {
throw new Error('fake error 1');
});
env.it('should be another test that fails because it throws an exception', function() {
this.runs(function () {
throw new Error('fake error 2');
});
this.runs(function () {
this.expect(true).toEqual(true);
});
});
env.it('should be a passing test that runs after exceptions are thrown', function() {
this.expect(true).toEqual(true);
});
env.it('should be another test that fails because it throws an exception after a wait', function() {
this.runs(function () {
var foo = 'foo';
});
this.waits(250);
this.runs(function () {
throw new Error('fake error 3');
});
});
env.it('should be a passing test that runs after exceptions are thrown from a async test', function() {
this.expect(true).toEqual(true);
});
});
var runner = env.currentRunner();
suite.execute();
fakeTimer.tick(2500);
var suiteResults = suite.results();
var specResults = suiteResults.getItems();
expect(suiteResults.passed()).toEqual(false);
//
expect(specResults.length).toEqual(5);
expect(specResults[0].passed()).toMatch(false);
var blockResults = specResults[0].getItems();
expect(blockResults[0].passed()).toEqual(false);
expect(blockResults[0].message).toMatch(/fake error 1/);
expect(specResults[1].passed()).toEqual(false);
blockResults = specResults[1].getItems();
expect(blockResults[0].passed()).toEqual(false);
expect(blockResults[0].message).toMatch(/fake error 2/);
expect(blockResults[1].passed()).toEqual(true);
expect(specResults[2].passed()).toEqual(true);
expect(specResults[3].passed()).toEqual(false);
blockResults = specResults[3].getItems();
expect(blockResults[0].message).toMatch(/fake error 3/);
expect(specResults[4].passed()).toEqual(true);
});
it("should handle exceptions thrown directly in top-level describe blocks and continue", function () {
var suite = env.describe("a top level describe block that throws an exception", function () {
env.it("is a test that should pass", function () {
this.expect(true).toEqual(true);
});
throw new Error("top level error");
});
suite.execute();
var suiteResults = suite.results();
var specResults = suiteResults.getItems();
expect(suiteResults.passed()).toEqual(false);
expect(specResults.length).toEqual(2);
expect(specResults[1].description).toMatch(/encountered a declaration exception/);
});
it("should handle exceptions thrown directly in nested describe blocks and continue", function () {
var suite = env.describe("a top level describe", function () {
env.describe("a mid-level describe that throws an exception", function () {
env.it("is a test that should pass", function () {
this.expect(true).toEqual(true);
});
throw new Error("a mid-level error");
});
});
suite.execute();
var suiteResults = suite.results();
var specResults = suiteResults.getItems();
expect(suiteResults.passed()).toEqual(false);
expect(specResults.length).toEqual(1);
var nestedSpecResults = specResults[0].getItems();
expect(nestedSpecResults.length).toEqual(2);
expect(nestedSpecResults[1].description).toMatch(/encountered a declaration exception/);
});
});
});

View File

@ -0,0 +1,103 @@
describe('jasmine.jsApiReporter', function() {
describe('results', function () {
var reporter, spec1, spec2, spec3, expectedSpec1Results, expectedSpec2Results;
var env;
var suite, nestedSuite, nestedSpec;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
suite = env.describe("top-level suite", function() {
spec1 = env.it("spec 1", function() {
this.expect(true).toEqual(true);
});
spec2 = env.it("spec 2", function() {
this.expect(true).toEqual(false);
});
nestedSuite = env.describe("nested suite", function() {
nestedSpec = env.it("nested spec", function() {
expect(true).toEqual(true);
});
});
spec3 = env.it("spec 3", function() {
this.log('some debug message');
});
});
reporter = new jasmine.JsApiReporter();
env.addReporter(reporter);
env.execute();
expectedSpec1Results = {
messages: spec1.results().getItems(),
result: "passed"
};
expectedSpec2Results = {
messages: spec2.results().getItems(),
result: "failed"
};
});
it('resultForSpec() should return the result for the given spec', function () {
expect(reporter.resultsForSpec(spec1.id)).toEqual(expectedSpec1Results);
expect(reporter.resultsForSpec(spec2.id)).toEqual(expectedSpec2Results);
});
it('results() should return a hash of all results, indexed by spec id', function () {
expect(reporter.results()[spec1.id]).toEqual(expectedSpec1Results);
expect(reporter.results()[spec2.id]).toEqual(expectedSpec2Results);
});
it("should return nested suites as children of their parents", function() {
expect(reporter.suites()).toEqual([
{ id: 0, name: 'top-level suite', type: 'suite',
children: [
{ id: 0, name: 'spec 1', type: 'spec', children: [ ] },
{ id: 1, name: 'spec 2', type: 'spec', children: [ ] },
{ id: 1, name: 'nested suite', type: 'suite',
children: [
{ id: 2, name: 'nested spec', type: 'spec', children: [ ] }
]
},
{ id: 3, name: 'spec 3', type: 'spec', children: [ ] }
]
}
]);
});
describe("#summarizeResult_", function() {
it("should summarize a passing result", function() {
var result = reporter.results()[spec1.id];
var summarizedResult = reporter.summarizeResult_(result);
expect(summarizedResult.result).toEqual('passed');
expect(summarizedResult.messages.length).toEqual(1);
expect(summarizedResult.messages[0].message).toEqual(result.messages[0].message);
expect(summarizedResult.messages[0].passed).toBeTruthy();
expect(summarizedResult.messages[0].type).toEqual('expect');
expect(summarizedResult.messages[0].text).toBeUndefined();
expect(summarizedResult.messages[0].trace.stack).toBeUndefined();
});
it("should have a stack trace for failing specs", function() {
var result = reporter.results()[spec2.id];
var summarizedResult = reporter.summarizeResult_(result);
expect(summarizedResult.result).toEqual('failed');
expect(summarizedResult.messages[0].trace.stack).toEqual(result.messages[0].trace.stack);
});
it("should have messages for specs with messages", function() {
var result = reporter.results()[spec3.id];
var summarizedResult = reporter.summarizeResult_(result);
expect(summarizedResult.result).toEqual('passed');
expect(summarizedResult.messages[0].type).toEqual('log');
expect(summarizedResult.messages[0].text).toEqual('some debug message');
});
});
});
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
describe("MockClock", function () {
beforeEach(function() {
jasmine.Clock.useMock();
});
describe("setTimeout", function () {
it("should mock the clock when useMock is in a beforeEach", function() {
var expected = false;
setTimeout(function() {
expected = true;
}, 30000);
expect(expected).toBe(false);
jasmine.Clock.tick(30001);
expect(expected).toBe(true);
});
});
describe("setInterval", function () {
it("should mock the clock when useMock is in a beforeEach", function() {
var interval = 0;
setInterval(function() {
interval++;
}, 30000);
expect(interval).toEqual(0);
jasmine.Clock.tick(30001);
expect(interval).toEqual(1);
jasmine.Clock.tick(30001);
expect(interval).toEqual(2);
jasmine.Clock.tick(1);
expect(interval).toEqual(2);
});
});
it("shouldn't complain if you call jasmine.Clock.useMock() more than once", function() {
jasmine.Clock.useMock();
});
});

View File

@ -0,0 +1,45 @@
describe("jasmine.MultiReporter", function() {
var multiReporter, fakeReporter1, fakeReporter2;
beforeEach(function() {
multiReporter = new jasmine.MultiReporter();
fakeReporter1 = jasmine.createSpyObj("fakeReporter1", ["reportSpecResults"]);
fakeReporter2 = jasmine.createSpyObj("fakeReporter2", ["reportSpecResults", "reportRunnerStarting"]);
multiReporter.addReporter(fakeReporter1);
multiReporter.addReporter(fakeReporter2);
});
it("should support all the method calls that jasmine.Reporter supports", function() {
var delegate = {};
multiReporter.addReporter(delegate);
this.addMatchers({
toDelegateMethod: function(methodName) {
delegate[methodName] = jasmine.createSpy(methodName);
this.actual[methodName]("whatever argument");
return delegate[methodName].wasCalled &&
delegate[methodName].mostRecentCall.args.length == 1 &&
delegate[methodName].mostRecentCall.args[0] == "whatever argument";
}
});
expect(multiReporter).toDelegateMethod('reportRunnerStarting');
expect(multiReporter).toDelegateMethod('reportRunnerResults');
expect(multiReporter).toDelegateMethod('reportSuiteResults');
expect(multiReporter).toDelegateMethod('reportSpecStarting');
expect(multiReporter).toDelegateMethod('reportSpecResults');
expect(multiReporter).toDelegateMethod('log');
});
it("should delegate to any and all subreporters", function() {
multiReporter.reportSpecResults('blah', 'foo');
expect(fakeReporter1.reportSpecResults).toHaveBeenCalledWith('blah', 'foo');
expect(fakeReporter2.reportSpecResults).toHaveBeenCalledWith('blah', 'foo');
});
it("should quietly skip delegating to any subreporters which lack the given method", function() {
multiReporter.reportRunnerStarting('blah', 'foo');
expect(fakeReporter2.reportRunnerStarting).toHaveBeenCalledWith('blah', 'foo');
});
});

View File

@ -0,0 +1,54 @@
describe('jasmine.NestedResults', function() {
it('#addResult increments counters', function() {
// Leaf case
var results = new jasmine.NestedResults();
results.addResult(new jasmine.ExpectationResult({
matcherName: "foo", passed: true, message: 'Passed.', actual: 'bar', expected: 'bar'}
));
expect(results.getItems().length).toEqual(1);
expect(results.totalCount).toEqual(1);
expect(results.passedCount).toEqual(1);
expect(results.failedCount).toEqual(0);
results.addResult(new jasmine.ExpectationResult({
matcherName: "baz", passed: false, message: 'FAIL.', actual: "corge", expected: "quux"
}));
expect(results.getItems().length).toEqual(2);
expect(results.totalCount).toEqual(2);
expect(results.passedCount).toEqual(1);
expect(results.failedCount).toEqual(1);
});
it('should roll up counts for nested results', function() {
// Branch case
var leafResultsOne = new jasmine.NestedResults();
leafResultsOne.addResult(new jasmine.ExpectationResult({
matcherName: "toSomething", passed: true, message: 'message', actual: '', expected:''
}));
leafResultsOne.addResult(new jasmine.ExpectationResult({
matcherName: "toSomethingElse", passed: false, message: 'message', actual: 'a', expected: 'b'
}));
var leafResultsTwo = new jasmine.NestedResults();
leafResultsTwo.addResult(new jasmine.ExpectationResult({
matcherName: "toSomething", passed: true, message: 'message', actual: '', expected: ''
}));
leafResultsTwo.addResult(new jasmine.ExpectationResult({
matcherName: "toSomethineElse", passed: false, message: 'message', actual: 'c', expected: 'd'
}));
var branchResults = new jasmine.NestedResults();
branchResults.addResult(leafResultsOne);
branchResults.addResult(leafResultsTwo);
expect(branchResults.getItems().length).toEqual(2);
expect(branchResults.totalCount).toEqual(4);
expect(branchResults.passedCount).toEqual(2);
expect(branchResults.failedCount).toEqual(2);
});
});

View File

@ -0,0 +1,94 @@
describe("jasmine.pp", function () {
it("should wrap strings in single quotes", function() {
expect(jasmine.pp("some string")).toEqual("'some string'");
expect(jasmine.pp("som' string")).toEqual("'som' string'");
});
it("should stringify primitives properly", function() {
expect(jasmine.pp(true)).toEqual("true");
expect(jasmine.pp(false)).toEqual("false");
expect(jasmine.pp(null)).toEqual("null");
expect(jasmine.pp(jasmine.undefined)).toEqual("undefined");
expect(jasmine.pp(3)).toEqual("3");
expect(jasmine.pp(-3.14)).toEqual("-3.14");
});
it("should stringify arrays properly", function() {
expect(jasmine.pp([1, 2])).toEqual("[ 1, 2 ]");
expect(jasmine.pp([1, 'foo', {}, jasmine.undefined, null])).toEqual("[ 1, 'foo', { }, undefined, null ]");
});
it("should indicate circular array references", function() {
var array1 = [1, 2];
var array2 = [array1];
array1.push(array2);
expect(jasmine.pp(array1)).toEqual("[ 1, 2, [ <circular reference: Array> ] ]");
});
it("should stringify objects properly", function() {
expect(jasmine.pp({foo: 'bar'})).toEqual("{ foo : 'bar' }");
expect(jasmine.pp({foo:'bar', baz:3, nullValue: null, undefinedValue: jasmine.undefined})).toEqual("{ foo : 'bar', baz : 3, nullValue : null, undefinedValue : undefined }");
expect(jasmine.pp({foo: function () {
}, bar: [1, 2, 3]})).toEqual("{ foo : Function, bar : [ 1, 2, 3 ] }");
});
it("should stringify RegExp objects properly", function() {
expect(jasmine.pp(/x|y|z/)).toEqual("/x|y|z/");
});
it("should indicate circular object references", function() {
var sampleValue = {foo: 'hello'};
sampleValue.nested = sampleValue;
expect(jasmine.pp(sampleValue)).toEqual("{ foo : 'hello', nested : <circular reference: Object> }");
});
it("should indicate getters on objects as such", function() {
var sampleValue = {id: 1};
if (sampleValue.__defineGetter__) {
//not supported in IE!
sampleValue.__defineGetter__('calculatedValue', function() {
throw new Error("don't call me!");
});
}
if (sampleValue.__defineGetter__) {
expect(jasmine.pp(sampleValue)).toEqual("{ id : 1, calculatedValue : <getter> }");
}
else {
expect(jasmine.pp(sampleValue)).toEqual("{ id : 1 }");
}
});
it('should not do HTML escaping of strings', function() {
expect(jasmine.pp('some <b>html string</b> &', false)).toEqual('\'some <b>html string</b> &\'');
});
it("should abbreviate the global (usually window) object", function() {
expect(jasmine.pp(jasmine.getGlobal())).toEqual("<global>");
});
it("should stringify Date objects properly", function() {
var now = new Date();
expect(jasmine.pp(now)).toEqual("Date(" + now.toString() + ")");
});
it("should stringify spy objects properly", function() {
var TestObject = {
someFunction: function() {
}
};
spyOn(TestObject, 'someFunction');
expect(jasmine.pp(TestObject.someFunction)).toEqual("spy on someFunction");
expect(jasmine.pp(jasmine.createSpy("something"))).toEqual("spy on something");
});
it("should stringify objects that implement jasmineToString", function () {
var obj = {
jasmineToString: function () { return "strung"; }
};
expect(jasmine.pp(obj)).toEqual("strung");
});
});

View File

@ -0,0 +1,23 @@
describe("jasmine.Queue", function() {
it("should not call itself recursively, so we don't get stack overflow errors", function() {
var queue = new jasmine.Queue(new jasmine.Env());
queue.add(new jasmine.Block(null, function() {}));
queue.add(new jasmine.Block(null, function() {}));
queue.add(new jasmine.Block(null, function() {}));
queue.add(new jasmine.Block(null, function() {}));
var nestCount = 0;
var maxNestCount = 0;
var nextCallCount = 0;
queue.next_ = function() {
nestCount++;
if (nestCount > maxNestCount) maxNestCount = nestCount;
jasmine.Queue.prototype.next_.apply(queue, arguments);
nestCount--;
};
queue.start();
expect(maxNestCount).toEqual(1);
});
});

View File

@ -0,0 +1,56 @@
describe('jasmine.Reporter', function() {
var env;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
});
it('should get called from the test runner', function() {
env.describe('Suite for JSON Reporter with Callbacks', function () {
env.it('should be a test', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
env.it('should be a failing test', function() {
this.runs(function () {
this.expect(false).toEqual(true);
});
});
});
env.describe('Suite for JSON Reporter with Callbacks 2', function () {
env.it('should be a test', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
});
var foo = 0;
var bar = 0;
var baz = 0;
env.addReporter({
reportSpecResults: function() {
foo++;
},
reportSuiteResults: function() {
bar++;
},
reportRunnerResults: function() {
baz++;
}
});
var runner = env.currentRunner();
runner.execute();
expect(foo).toEqual(3); // 'foo was expected to be 3, was ' + foo);
expect(bar).toEqual(2); // 'bar was expected to be 2, was ' + bar);
expect(baz).toEqual(1); // 'baz was expected to be 1, was ' + baz);
});
});

View File

@ -0,0 +1,267 @@
describe('RunnerTest', function() {
var fakeTimer;
var env;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
fakeTimer = new jasmine.FakeTimer();
env.setTimeout = fakeTimer.setTimeout;
env.clearTimeout = fakeTimer.clearTimeout;
env.setInterval = fakeTimer.setInterval;
env.clearInterval = fakeTimer.clearInterval;
});
describe('beforeEach', function() {
it('should run before each spec for all suites', function () {
var foo;
env.beforeEach(function () {
foo = 0;
});
env.describe('suite 1', function () {
env.it('test 1-1', function() {
foo++;
this.expect(foo).toEqual(1);
});
env.it('test 1-2', function() {
foo++;
this.expect(foo).toEqual(1);
});
});
env.describe('suite 2', function () {
env.it('test 2-1', function() {
foo++;
this.expect(foo).toEqual(1);
});
});
env.currentRunner().execute();
var runnerResults = env.currentRunner().results();
expect(runnerResults.totalCount).toEqual(3);
expect(runnerResults.passedCount).toEqual(3);
});
it('should provide all specs', function () {
var foo;
env.beforeEach(function () {
foo = 0;
});
env.describe('suite 1', function () {
env.it('test 1-1', function() {
foo++;
this.expect(foo).toEqual(1);
});
env.it('test 1-2', function() {
foo++;
this.expect(foo).toEqual(1);
});
});
env.describe('suite 2', function () {
env.it('test 2-1', function() {
foo++;
this.expect(foo).toEqual(1);
});
});
env.currentRunner().execute();
expect(env.currentRunner().specs().length).toEqual(3);
});
});
describe('afterEach', function() {
it('should run after each spec for all suites', function () {
var foo = 3;
env.afterEach(function () {
foo = foo - 1;
});
env.describe('suite 1', function () {
env.it('test 1-1', function() {
this.expect(foo).toEqual(3);
});
env.it('test 1-2', function() {
this.expect(foo).toEqual(2);
});
});
env.describe('suite 2', function () {
env.it('test 2-1', function() {
this.expect(foo).toEqual(1);
});
});
env.currentRunner().execute();
var runnerResults = env.currentRunner().results();
expect(runnerResults.totalCount).toEqual(3);
expect(runnerResults.passedCount).toEqual(3);
});
});
it('should run child suites and specs and generate results when execute is called', function() {
env.describe('one suite description', function () {
env.it('should be a test', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
});
env.describe('another suite description', function () {
env.it('should be another test', function() {
this.runs(function () {
this.expect(true).toEqual(false);
});
});
});
env.currentRunner().execute();
var runnerResults = env.currentRunner().results();
expect(runnerResults.totalCount).toEqual(2);
expect(runnerResults.passedCount).toEqual(1);
expect(runnerResults.failedCount).toEqual(1);
});
it('should ignore suites that have been x\'d', function() {
env.xdescribe('one suite description', function () {
env.it('should be a test', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
});
env.describe('another suite description', function () {
env.it('should be another test', function() {
this.runs(function () {
this.expect(true).toEqual(false);
});
});
});
env.currentRunner().execute();
var runnerResults = env.currentRunner().results();
expect(runnerResults.totalCount).toEqual(1);
expect(runnerResults.passedCount).toEqual(0);
expect(runnerResults.failedCount).toEqual(1);
});
it('should roll up results from all specs', function() {
env.describe('one suite description', function () {
env.it('should be a test', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
});
env.describe('another suite description', function () {
env.it('should be another test', function() {
this.runs(function () {
this.expect(true).toEqual(false);
});
});
});
env.currentRunner().execute();
var results = env.currentRunner().results();
expect(results.totalCount).toEqual(2);
expect(results.passedCount).toEqual(1);
expect(results.failedCount).toEqual(1);
});
describe('reporting', function () {
var fakeReporter;
beforeEach(function () {
fakeReporter = jasmine.createSpyObj("fakeReporter", ["log", "reportRunnerStarting", "reportRunnerResults"]);
env.addReporter(fakeReporter);
});
it('should report runner results when the runner has completed running', function() {
env.describe('one suite description', function () {
env.it('should be a test', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
});
env.describe('another suite description', function () {
env.it('should be another test', function() {
this.waits(200);
this.runs(function () {
this.expect(true).toEqual(false);
});
});
});
env.currentRunner().execute();
expect(fakeReporter.reportRunnerResults).not.toHaveBeenCalled();
fakeTimer.tick(200);
//This blows up the JSApiReporter.
//expect(fakeReporter.reportRunnerResults).toHaveBeenCalledWith(env.currentRunner);
expect(fakeReporter.reportRunnerResults).toHaveBeenCalled();
expect(fakeReporter.reportRunnerResults.mostRecentCall.args[0].results()).toEqual(env.currentRunner().results());
});
});
it("should report when the tests start running", function() {
var fakeReporter = jasmine.createSpyObj("fakeReporter", ["log", "reportRunnerStarting"]);
env.addReporter(fakeReporter);
var runner = new jasmine.Runner(env);
runner.arbitraryVariable = 'foo';
spyOn(runner.queue, 'start');
expect(fakeReporter.reportRunnerStarting).not.toHaveBeenCalled();
runner.execute();
expect(fakeReporter.reportRunnerStarting).toHaveBeenCalled();
var reportedRunner = fakeReporter.reportRunnerStarting.mostRecentCall.args[0];
expect(reportedRunner.arbitraryVariable).toEqual('foo');
expect(runner.queue.start).toHaveBeenCalled();
});
describe("when suites are nested", function() {
var suite1, suite2, suite3;
function suiteNames(suites) {
var suiteDescriptions = [];
for (var i = 0; i < suites.length; i++) {
suiteDescriptions.push(suites[i].getFullName());
}
return suiteDescriptions;
}
beforeEach(function() {
suite1 = env.describe("suite 1", function() {
suite2 = env.describe("suite 2", function() {
});
});
suite3 = env.describe("suite 3", function() {});
});
it("#suites should return a flat array of all suites, including nested suites", function() {
var suites = env.currentRunner().suites();
expect(suiteNames(suites)).toEqual([suite1.getFullName(), suite2.getFullName(), suite3.getFullName()]);
});
it("#topLevelSuites should return a flat array of all top-level suites only", function() {
var suites = env.currentRunner().topLevelSuites();
expect(suiteNames(suites)).toEqual([suite1.getFullName(), suite3.getFullName()]);
});
});
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,124 @@
describe('Spec', function () {
var env, suite;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
suite = new jasmine.Suite(env, 'suite 1');
});
describe('initialization', function () {
it('should raise an error if an env is not passed', function () {
try {
new jasmine.Spec();
}
catch (e) {
expect(e.message).toEqual('jasmine.Env() required');
}
});
it('should raise an error if a suite is not passed', function () {
try {
new jasmine.Spec(env);
}
catch (e) {
expect(e.message).toEqual('jasmine.Suite() required');
}
});
it('should assign sequential ids for specs belonging to the same env', function () {
var spec1 = new jasmine.Spec(env, suite);
var spec2 = new jasmine.Spec(env, suite);
var spec3 = new jasmine.Spec(env, suite);
expect(spec1.id).toEqual(0);
expect(spec2.id).toEqual(1);
expect(spec3.id).toEqual(2);
});
});
it('getFullName returns suite & spec description', function () {
var spec = new jasmine.Spec(env, suite, 'spec 1');
expect(spec.getFullName()).toEqual('suite 1 spec 1.');
});
describe('results', function () {
var spec, results;
beforeEach(function () {
spec = new jasmine.Spec(env, suite);
results = spec.results();
expect(results.totalCount).toEqual(0);
spec.runs(function () {
this.expect(true).toEqual(true);
this.expect(true).toEqual(true);
});
});
it('results shows the total number of expectations for each spec after execution', function () {
expect(results.totalCount).toEqual(0);
spec.execute();
expect(results.totalCount).toEqual(2);
});
it('results shows the number of passed expectations for each spec after execution', function () {
expect(results.passedCount).toEqual(0);
spec.execute();
expect(results.passedCount).toEqual(2);
});
it('results shows the number of failed expectations for each spec after execution', function () {
spec.runs(function () {
this.expect(true).toEqual(false);
});
expect(results.failedCount).toEqual(0);
spec.execute();
expect(results.failedCount).toEqual(1);
});
describe('results.passed', function () {
it('is true if all spec expectations pass', function () {
spec.runs(function () {
this.expect(true).toEqual(true);
});
spec.execute();
expect(results.passed()).toEqual(true);
});
it('is false if one spec expectation fails', function () {
spec.runs(function () {
this.expect(true).toEqual(false);
});
spec.execute();
expect(results.passed()).toEqual(false);
});
it('a spec with no expectations will return true', function () {
var specWithoutExpectations = new jasmine.Spec(env, suite);
specWithoutExpectations.runs(function() {
});
specWithoutExpectations.execute();
expect(results.passed()).toEqual(true);
});
it('an unexecuted spec will return true', function () {
expect(results.passed()).toEqual(true);
});
});
it("includes log messages, which may contain arbitary objects", function() {
spec.runs(function() {
this.log("here's some log message", {key: 'value'}, 123);
});
spec.execute();
var items = results.getItems();
expect(items).toEqual([
jasmine.any(jasmine.ExpectationResult),
jasmine.any(jasmine.ExpectationResult),
jasmine.any(jasmine.MessageResult)
]);
var logResult = items[2];
expect(logResult.values).toEqual(["here's some log message", {key: 'value'}, 123]);
});
});
});

View File

@ -0,0 +1,201 @@
describe('Spies', function () {
it('should replace the specified function with a spy object', function() {
var originalFunctionWasCalled = false;
var TestClass = {
someFunction: function() {
originalFunctionWasCalled = true;
}
};
this.spyOn(TestClass, 'someFunction');
expect(TestClass.someFunction.wasCalled).toEqual(false);
expect(TestClass.someFunction.callCount).toEqual(0);
TestClass.someFunction('foo');
expect(TestClass.someFunction.wasCalled).toEqual(true);
expect(TestClass.someFunction.callCount).toEqual(1);
expect(TestClass.someFunction.mostRecentCall.args).toEqual(['foo']);
expect(TestClass.someFunction.mostRecentCall.object).toEqual(TestClass);
expect(originalFunctionWasCalled).toEqual(false);
TestClass.someFunction('bar');
expect(TestClass.someFunction.callCount).toEqual(2);
expect(TestClass.someFunction.mostRecentCall.args).toEqual(['bar']);
});
it('should allow you to view args for a particular call', function() {
var originalFunctionWasCalled = false;
var TestClass = {
someFunction: function() {
originalFunctionWasCalled = true;
}
};
this.spyOn(TestClass, 'someFunction');
TestClass.someFunction('foo');
TestClass.someFunction('bar');
expect(TestClass.someFunction.calls[0].args).toEqual(['foo']);
expect(TestClass.someFunction.calls[1].args).toEqual(['bar']);
expect(TestClass.someFunction.mostRecentCall.args).toEqual(['bar']);
});
it('should be possible to call through to the original method, or return a specific result', function() {
var originalFunctionWasCalled = false;
var passedArgs;
var passedObj;
var TestClass = {
someFunction: function() {
originalFunctionWasCalled = true;
passedArgs = arguments;
passedObj = this;
return "return value from original function";
}
};
this.spyOn(TestClass, 'someFunction').andCallThrough();
var result = TestClass.someFunction('arg1', 'arg2');
expect(result).toEqual("return value from original function");
expect(originalFunctionWasCalled).toEqual(true);
expect(passedArgs).toEqual(['arg1', 'arg2']);
expect(passedObj).toEqual(TestClass);
expect(TestClass.someFunction.wasCalled).toEqual(true);
});
it('should be possible to return a specific value', function() {
var originalFunctionWasCalled = false;
var TestClass = {
someFunction: function() {
originalFunctionWasCalled = true;
return "return value from original function";
}
};
this.spyOn(TestClass, 'someFunction').andReturn("some value");
originalFunctionWasCalled = false;
var result = TestClass.someFunction('arg1', 'arg2');
expect(result).toEqual("some value");
expect(originalFunctionWasCalled).toEqual(false);
});
it('should be possible to throw a specific error', function() {
var originalFunctionWasCalled = false;
var TestClass = {
someFunction: function() {
originalFunctionWasCalled = true;
return "return value from original function";
}
};
this.spyOn(TestClass, 'someFunction').andThrow(new Error('fake error'));
var exception;
try {
TestClass.someFunction('arg1', 'arg2');
} catch (e) {
exception = e;
}
expect(exception.message).toEqual('fake error');
expect(originalFunctionWasCalled).toEqual(false);
});
it('should be possible to call a specified function', function() {
var originalFunctionWasCalled = false;
var fakeFunctionWasCalled = false;
var passedArgs;
var passedObj;
var TestClass = {
someFunction: function() {
originalFunctionWasCalled = true;
return "return value from original function";
}
};
this.spyOn(TestClass, 'someFunction').andCallFake(function() {
fakeFunctionWasCalled = true;
passedArgs = arguments;
passedObj = this;
return "return value from fake function";
});
var result = TestClass.someFunction('arg1', 'arg2');
expect(result).toEqual("return value from fake function");
expect(originalFunctionWasCalled).toEqual(false);
expect(fakeFunctionWasCalled).toEqual(true);
expect(passedArgs).toEqual(['arg1', 'arg2']);
expect(passedObj).toEqual(TestClass);
expect(TestClass.someFunction.wasCalled).toEqual(true);
});
it('is torn down when this.removeAllSpies is called', function() {
var originalFunctionWasCalled = false;
var TestClass = {
someFunction: function() {
originalFunctionWasCalled = true;
}
};
this.spyOn(TestClass, 'someFunction');
TestClass.someFunction('foo');
expect(originalFunctionWasCalled).toEqual(false);
this.removeAllSpies();
TestClass.someFunction('foo');
expect(originalFunctionWasCalled).toEqual(true);
});
it('calls removeAllSpies during spec finish', function() {
var test = new jasmine.Spec(new jasmine.Env(), {}, 'sample test');
this.spyOn(test, 'removeAllSpies');
test.finish();
expect(test.removeAllSpies).wasCalled();
});
it('throws an exception when some method is spied on twice', function() {
var TestClass = { someFunction: function() {
} };
this.spyOn(TestClass, 'someFunction');
var exception;
try {
this.spyOn(TestClass, 'someFunction');
} catch (e) {
exception = e;
}
expect(exception).toBeDefined();
});
it('should be able to reset a spy', function() {
var TestClass = { someFunction: function() {} };
this.spyOn(TestClass, 'someFunction');
expect(TestClass.someFunction).not.toHaveBeenCalled();
TestClass.someFunction();
expect(TestClass.someFunction).toHaveBeenCalled();
TestClass.someFunction.reset();
expect(TestClass.someFunction).not.toHaveBeenCalled();
expect(TestClass.someFunction.callCount).toEqual(0);
});
describe("createSpyObj", function() {
it("should create an object with a bunch of spy methods when you call jasmine.createSpyObj()", function() {
var spyObj = jasmine.createSpyObj('BaseName', ['method1', 'method2']);
expect(spyObj).toEqual({ method1: jasmine.any(Function), method2: jasmine.any(Function)});
expect(spyObj.method1.identity).toEqual('BaseName.method1');
expect(spyObj.method2.identity).toEqual('BaseName.method2');
});
it("should throw if you do not pass an array argument", function() {
expect(function() {
jasmine.createSpyObj('BaseName');
}).toThrow('createSpyObj requires a non-empty array of method names to create spies for');
});
it("should throw if you pass an empty array argument", function() {
expect(function() {
jasmine.createSpyObj('BaseName');
}).toThrow('createSpyObj requires a non-empty array of method names to create spies for');
});
});
});

View File

@ -0,0 +1,120 @@
describe('Suite', function() {
var fakeTimer;
var env;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
fakeTimer = new jasmine.FakeTimer();
env.setTimeout = fakeTimer.setTimeout;
env.clearTimeout = fakeTimer.clearTimeout;
env.setInterval = fakeTimer.setInterval;
env.clearInterval = fakeTimer.clearInterval;
});
describe('Specs', function () {
var suite;
beforeEach(function() {
suite = env.describe('Suite 1', function () {
env.it('Spec 1', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
env.it('Spec 2', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
env.describe('Suite 2', function () {
env.it('Spec 3', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
});
env.it('Spec 4', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
});
});
it('#specs should return all immediate children that are specs.', function () {
var suiteSpecs = suite.specs();
expect(suiteSpecs.length).toEqual(3);
expect(suiteSpecs[0].description).toEqual('Spec 1');
expect(suiteSpecs[1].description).toEqual('Spec 2');
expect(suiteSpecs[2].description).toEqual('Spec 4');
});
it("#suites should return all immediate children that are suites.", function() {
var nestedSuites = suite.suites();
expect(nestedSuites.length).toEqual(1);
expect(nestedSuites[0].description).toEqual('Suite 2');
});
it("#children should return all immediate children including suites and specs.", function() {
var children = suite.children();
expect(children.length).toEqual(4);
expect(children[0].description).toEqual('Spec 1');
expect(children[1].description).toEqual('Spec 2');
expect(children[2].description).toEqual('Suite 2');
expect(children[3].description).toEqual('Spec 4');
});
});
describe('SpecCount', function () {
it('should keep a count of the number of specs that are run', function() {
var suite = env.describe('one suite description', function () {
env.it('should be a test', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
env.it('should be another test', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
env.it('should be a third test', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
});
expect(suite.specs().length).toEqual(3);
});
it('specCount should be correct even with runs/waits blocks', function() {
var suite = env.describe('one suite description', function () {
env.it('should be a test', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
env.it('should be another test', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
this.waits(10);
this.runs(function () {
this.expect(true).toEqual(true);
});
});
env.it('should be a third test', function() {
this.runs(function () {
this.expect(true).toEqual(true);
});
});
});
expect(suite.specs().length).toEqual(3);
});
});
});

View File

@ -0,0 +1,39 @@
describe("jasmine.util", function() {
describe("extend", function () {
it("should add properies to a destination object ", function() {
var destination = {baz: 'baz'};
jasmine.util.extend(destination, {
foo: 'foo', bar: 'bar'
});
expect(destination).toEqual({foo: 'foo', bar: 'bar', baz: 'baz'});
});
it("should replace properies that already exist on a destination object", function() {
var destination = {foo: 'foo'};
jasmine.util.extend(destination, {
foo: 'bar'
});
expect(destination).toEqual({foo: 'bar'});
jasmine.util.extend(destination, {
foo: null
});
expect(destination).toEqual({foo: null});
});
});
describe("isArray_", function() {
it("should return true if the argument is an array", function() {
expect(jasmine.isArray_([])).toBe(true);
expect(jasmine.isArray_(['a'])).toBe(true);
});
it("should return false if the argument is not an array", function() {
expect(jasmine.isArray_(undefined)).toBe(false);
expect(jasmine.isArray_({})).toBe(false);
expect(jasmine.isArray_(function() {})).toBe(false);
expect(jasmine.isArray_('foo')).toBe(false);
expect(jasmine.isArray_(5)).toBe(false);
expect(jasmine.isArray_(null)).toBe(false);
});
});
});

View File

@ -0,0 +1,118 @@
describe('WaitsForBlock', function () {
var env, suite, timeout, spec, message, onComplete, fakeTimer;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
suite = new jasmine.Suite(env, 'suite 1');
timeout = 1000;
spec = new jasmine.Spec(env, suite);
message = "some error message";
onComplete = jasmine.createSpy("onComplete");
});
describe("jasmine.VERBOSE", function() {
var jasmineVerboseOriginal;
beforeEach(function() {
jasmineVerboseOriginal = jasmine.VERBOSE;
spyOn(env.reporter, 'log');
});
it('do not show information if jasmine.VERBOSE is set to false', function () {
jasmine.VERBOSE = false;
var latchFunction = function() {
return true;
};
var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec);
expect(env.reporter.log).not.toHaveBeenCalled();
block.execute(onComplete);
expect(env.reporter.log).not.toHaveBeenCalled();
jasmine.VERBOSE = jasmineVerboseOriginal;
});
it('show information if jasmine.VERBOSE is set to true', function () {
jasmine.VERBOSE = true;
var latchFunction = function() {
return true;
};
var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec);
expect(env.reporter.log).not.toHaveBeenCalled();
block.execute(onComplete);
expect(env.reporter.log).toHaveBeenCalled();
jasmine.VERBOSE = jasmineVerboseOriginal;
});
});
it('onComplete should be called if the latchFunction returns true', function () {
var latchFunction = function() {
return true;
};
var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec);
expect(onComplete).not.toHaveBeenCalled();
block.execute(onComplete);
expect(onComplete).toHaveBeenCalled();
});
it('latchFunction should run in same scope as spec', function () {
var result;
var latchFunction = function() {
result = this.scopedValue;
};
spec.scopedValue = 'foo';
var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec);
block.execute(onComplete);
expect(result).toEqual('foo');
});
it('should fail spec and call onComplete if there is an error in the latchFunction', function() {
var latchFunction = jasmine.createSpy('latchFunction').andThrow('some error');
spyOn(spec, 'fail');
var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec);
block.execute(onComplete);
expect(spec.fail).toHaveBeenCalledWith('some error');
expect(onComplete).toHaveBeenCalled();
});
describe("if latchFunction returns false", function() {
var latchFunction, fakeTimer;
beforeEach(function() {
latchFunction = jasmine.createSpy('latchFunction').andReturn(false);
fakeTimer = new jasmine.FakeTimer();
env.setTimeout = fakeTimer.setTimeout;
env.clearTimeout = fakeTimer.clearTimeout;
env.setInterval = fakeTimer.setInterval;
env.clearInterval = fakeTimer.clearInterval;
});
it('latchFunction should be retried after 10 ms', function () {
var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec);
expect(latchFunction).not.toHaveBeenCalled();
block.execute(onComplete);
expect(latchFunction.callCount).toEqual(1);
fakeTimer.tick(5);
expect(latchFunction.callCount).toEqual(1);
fakeTimer.tick(5);
expect(latchFunction.callCount).toEqual(2);
});
it('onComplete should be called if latchFunction returns true before timeout', function () {
var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec);
expect(onComplete).not.toHaveBeenCalled();
block.execute(onComplete);
expect(onComplete).not.toHaveBeenCalled();
latchFunction.andReturn(true);
fakeTimer.tick(100);
expect(onComplete).toHaveBeenCalled();
});
it('spec should fail with the passed message if the timeout is reached (and not call onComplete)', function () {
spyOn(spec, 'fail');
var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec);
block.execute(onComplete);
expect(spec.fail).not.toHaveBeenCalled();
fakeTimer.tick(timeout);
expect(spec.fail).toHaveBeenCalled();
var failMessage = spec.fail.mostRecentCall.args[0].message;
expect(failMessage).toMatch(message);
expect(onComplete).toHaveBeenCalled();
});
});
});

View File

@ -0,0 +1,209 @@
describe("HtmlReporter", function() {
var env;
var htmlReporter;
var body;
var fakeDocument;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
body = document.createElement("body");
fakeDocument = { body: body, location: { search: "" } };
htmlReporter = new jasmine.HtmlReporter(fakeDocument);
});
function fakeSpec(name) {
return {
getFullName: function() {
return name;
}
};
}
function findElements(divs, withClass) {
var els = [];
for (var i = 0; i < divs.length; i++) {
if (divs[i].className == withClass) els.push(divs[i]);
}
return els;
}
function findElement(divs, withClass) {
var els = findElements(divs, withClass);
if (els.length > 0) {
return els[0];
}
throw new Error("couldn't find div with class " + withClass);
}
it("should run only specs beginning with spec parameter", function() {
fakeDocument.location.search = "?spec=run%20this";
expect(htmlReporter.specFilter(fakeSpec("run this"))).toBeTruthy();
expect(htmlReporter.specFilter(fakeSpec("not the right spec"))).toBeFalsy();
expect(htmlReporter.specFilter(fakeSpec("not run this"))).toBeFalsy();
});
describe("running without any specs", function() {
var runner;
beforeEach(function() {
runner = env.currentRunner();
env.addReporter(htmlReporter);
});
it("should not error", function() {
var exec = function() {
runner.execute();
};
expect(exec).not.toThrow();
});
});
describe('Matcher reporting', function() {
var getResultMessageDiv = function(body) {
var divs = body.getElementsByTagName("div");
for (var i = 0; i < divs.length; i++) {
if (divs[i].className.match(/resultMessage/)) {
return divs[i];
}
}
};
var runner, spec, fakeTimer;
beforeEach(function() {
fakeTimer = new jasmine.FakeTimer();
env.setTimeout = fakeTimer.setTimeout;
env.clearTimeout = fakeTimer.clearTimeout;
env.setInterval = fakeTimer.setInterval;
env.clearInterval = fakeTimer.clearInterval;
runner = env.currentRunner();
var suite = new jasmine.Suite(env, 'some suite');
runner.add(suite);
spec = new jasmine.Spec(env, suite, 'some spec');
suite.add(spec);
fakeDocument.location.search = "?";
env.addReporter(htmlReporter);
});
describe('toContain', function() {
it('should show actual and expected', function() {
spec.runs(function() {
this.expect('foo').toContain('bar');
});
runner.execute();
fakeTimer.tick(0);
var resultEl = getResultMessageDiv(body);
expect(resultEl.innerHTML).toMatch(/foo/);
expect(resultEl.innerHTML).toMatch(/bar/);
});
});
});
describe("failure messages (integration)", function() {
var spec, results, expectationResult;
it("should add the failure message to the DOM (non-toEquals matchers)", function() {
env.describe("suite", function() {
env.it("will have log messages", function() {
this.expect('a').toBeNull();
});
});
env.addReporter(htmlReporter);
env.execute();
var divs = body.getElementsByTagName("div");
var errorDiv = findElement(divs, 'resultMessage fail');
expect(errorDiv.innerHTML).toMatch(/Expected 'a' to be null/);
});
it("should add the failure message to the DOM (non-toEquals matchers) html escaping", function() {
env.describe("suite", function() {
env.it("will have log messages", function() {
this.expect('1 < 2').toBeNull();
});
});
env.addReporter(htmlReporter);
env.execute();
var divs = body.getElementsByTagName("div");
var errorDiv = findElement(divs, 'resultMessage fail');
expect(errorDiv.innerHTML).toMatch(/Expected '1 &lt; 2' to be null/);
});
});
describe("log messages", function() {
it("should appear in the report of a failed spec", function() {
env.describe("suite", function() {
env.it("will have log messages", function() {
this.log("this is a", "multipart log message");
this.expect(true).toBeFalsy();
});
});
env.addReporter(htmlReporter);
env.execute();
var divs = body.getElementsByTagName("div");
var errorDiv = findElement(divs, 'specDetail failed');
expect(errorDiv.innerHTML).toMatch("this is a multipart log message");
});
xit("should work on IE without console.log.apply", function() {
});
});
describe("duplicate example names", function() {
it("should report failures correctly", function() {
var suite1 = env.describe("suite", function() {
env.it("will have log messages", function() {
this.log("this one fails!");
this.expect(true).toBeFalsy();
});
});
var suite2 = env.describe("suite", function() {
env.it("will have log messages", function() {
this.log("this one passes!");
this.expect(true).toBeTruthy();
});
});
env.addReporter(htmlReporter);
env.execute();
var divs = body.getElementsByTagName("div");
var failedSpecDiv = findElement(divs, 'specDetail failed');
expect(failedSpecDiv.className).toEqual('specDetail failed');
expect(failedSpecDiv.innerHTML).toContain("this one fails!");
expect(failedSpecDiv.innerHTML).not.toContain("this one passes!");
});
});
describe('#reportSpecStarting', function() {
beforeEach(function() {
env.describe("suite 1", function() {
env.it("spec 1", function() {
});
});
spyOn(htmlReporter, 'log').andCallThrough();
});
it('DOES NOT log running specs by default', function() {
env.addReporter(htmlReporter);
env.execute();
expect(htmlReporter.log).not.toHaveBeenCalled();
});
it('logs running specs when log_running_specs is true', function() {
htmlReporter.logRunningSpecs = true;
env.addReporter(htmlReporter);
env.execute();
expect(htmlReporter.log).toHaveBeenCalledWith('>> Jasmine Running suite 1 spec 1...');
});
});
});

View File

@ -0,0 +1,38 @@
describe("MatchersSpec - HTML Dependent", function () {
var env, spec;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
var suite = env.describe("suite", function() {
spec = env.it("spec", function() {
});
});
spyOn(spec, 'addMatcherResult');
this.addMatchers({
toPass: function() {
return lastResult().passed();
},
toFail: function() {
return !lastResult().passed();
}
});
});
function match(value) {
return spec.expect(value);
}
function lastResult() {
return spec.addMatcherResult.mostRecentCall.args[0];
}
it("toEqual with DOM nodes", function() {
var nodeA = document.createElement('div');
var nodeB = document.createElement('div');
expect((match(nodeA).toEqual(nodeA))).toPass();
expect((match(nodeA).toEqual(nodeB))).toFail();
});
});

View File

@ -0,0 +1,8 @@
describe("jasmine.pp (HTML Dependent)", function () {
it("should stringify HTML nodes properly", function() {
var sampleNode = document.createElement('div');
sampleNode.innerHTML = 'foo<b>bar</b>';
expect(jasmine.pp(sampleNode)).toEqual("HTMLNode");
expect(jasmine.pp({foo: sampleNode})).toEqual("{ foo : HTMLNode }");
});
});

View File

@ -0,0 +1,239 @@
describe("TrivialReporter", function() {
var env;
var trivialReporter;
var body;
var fakeDocument;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
body = document.createElement("body");
fakeDocument = { body: body, location: { search: "" } };
trivialReporter = new jasmine.TrivialReporter(fakeDocument);
});
function fakeSpec(name) {
return {
getFullName: function() {
return name;
}
};
}
function findElements(divs, withClass) {
var els = [];
for (var i = 0; i < divs.length; i++) {
if (divs[i].className == withClass) els.push(divs[i]);
}
return els;
}
function findElement(divs, withClass) {
var els = findElements(divs, withClass);
if (els.length > 0) {
return els[0];
}
throw new Error("couldn't find div with class " + withClass);
}
it("should run only specs beginning with spec parameter", function() {
fakeDocument.location.search = "?spec=run%20this";
expect(trivialReporter.specFilter(fakeSpec("run this"))).toBeTruthy();
expect(trivialReporter.specFilter(fakeSpec("not the right spec"))).toBeFalsy();
expect(trivialReporter.specFilter(fakeSpec("not run this"))).toBeFalsy();
});
it("should display empty divs for every suite when the runner is starting", function() {
trivialReporter.reportRunnerStarting({
env: env,
suites: function() {
return [ new jasmine.Suite({}, "suite 1", null, null) ];
}
});
var divs = findElements(body.getElementsByTagName("div"), "suite");
expect(divs.length).toEqual(1);
expect(divs[0].innerHTML).toContain("suite 1");
});
describe('Matcher reporting', function () {
var getResultMessageDiv = function (body) {
var divs = body.getElementsByTagName("div");
for (var i = 0; i < divs.length; i++) {
if (divs[i].className.match(/resultMessage/)) {
return divs[i];
}
}
};
var runner, spec, fakeTimer;
beforeEach(function () {
fakeTimer = new jasmine.FakeTimer();
env.setTimeout = fakeTimer.setTimeout;
env.clearTimeout = fakeTimer.clearTimeout;
env.setInterval = fakeTimer.setInterval;
env.clearInterval = fakeTimer.clearInterval;
runner = env.currentRunner();
var suite = new jasmine.Suite(env, 'some suite');
runner.add(suite);
spec = new jasmine.Spec(env, suite, 'some spec');
suite.add(spec);
fakeDocument.location.search = "?";
env.addReporter(trivialReporter);
});
describe('toContain', function () {
it('should show actual and expected', function () {
spec.runs(function () {
this.expect('foo').toContain('bar');
});
runner.execute();
fakeTimer.tick(0);
var resultEl = getResultMessageDiv(body);
expect(resultEl.innerHTML).toMatch(/foo/);
expect(resultEl.innerHTML).toMatch(/bar/);
});
});
});
describe("failure messages (integration)", function () {
var spec, results, expectationResult;
beforeEach(function() {
results = {
passed: function() {
return false;
},
getItems: function() {
}};
var suite1 = new jasmine.Suite(env, "suite 1", null, null);
spec = {
suite: suite1,
getFullName: function() {
return "foo";
},
results: function() {
return results;
}
};
trivialReporter.reportRunnerStarting({
env: env,
suites: function() {
return [ suite1 ];
}
});
});
it("should add the failure message to the DOM (non-toEquals matchers)", function() {
expectationResult = new jasmine.ExpectationResult({
matcherName: "toBeNull", passed: false, message: "Expected 'a' to be null, but it was not"
});
spyOn(results, 'getItems').andReturn([expectationResult]);
trivialReporter.reportSpecResults(spec);
var divs = body.getElementsByTagName("div");
var errorDiv = findElement(divs, 'resultMessage fail');
expect(errorDiv.innerHTML).toEqual("Expected 'a' to be null, but it was not");
});
it("should add the failure message to the DOM (non-toEquals matchers) html escaping", function() {
expectationResult = new jasmine.ExpectationResult({
matcherName: "toBeNull", passed: false, message: "Expected '1 < 2' to <b>e null, & it was not"
});
spyOn(results, 'getItems').andReturn([expectationResult]);
trivialReporter.reportSpecResults(spec);
var divs = body.getElementsByTagName("div");
var errorDiv = findElement(divs, 'resultMessage fail');
expect(errorDiv.innerHTML).toEqual("Expected '1 &lt; 2' to &lt;b&gt;e null, &amp; it was not");
});
});
describe("log messages", function() {
it("should appear in the report", function() {
env.describe("suite", function() {
env.it("will have log messages", function() {
this.log("this is a", "multipart log message");
});
});
env.addReporter(trivialReporter);
env.execute();
var divs = body.getElementsByTagName("div");
var errorDiv = findElement(divs, 'resultMessage log');
expect(errorDiv.innerHTML).toEqual("this is a multipart log message");
});
xit("should work on IE without console.log.apply", function() {
});
});
describe("duplicate example names", function() {
it("should report failures correctly", function() {
var suite1 = env.describe("suite", function() {
env.it("will have log messages", function() {
this.log("this one fails!");
this.expect(true).toBeFalsy();
});
});
var suite2 = env.describe("suite", function() {
env.it("will have log messages", function() {
this.log("this one passes!");
this.expect(true).toBeTruthy();
});
});
env.addReporter(trivialReporter);
env.execute();
var divs = body.getElementsByTagName("div");
var passedSpecDiv = findElement(divs, 'suite passed');
expect(passedSpecDiv.className).toEqual('suite passed');
expect(passedSpecDiv.innerHTML).toContain("this one passes!");
expect(passedSpecDiv.innerHTML).not.toContain("this one fails!");
var failedSpecDiv = findElement(divs, 'suite failed');
expect(failedSpecDiv.className).toEqual('suite failed');
expect(failedSpecDiv.innerHTML).toContain("this one fails!");
expect(failedSpecDiv.innerHTML).not.toContain("this one passes!");
});
});
describe('#reportSpecStarting', function() {
var spec1;
beforeEach(function () {
env.describe("suite 1", function() {
spec1 = env.it("spec 1", function() {
});
});
});
it('DOES NOT log running specs by default', function() {
spyOn(trivialReporter, 'log');
trivialReporter.reportSpecStarting(spec1);
expect(trivialReporter.log).not.toHaveBeenCalled();
});
it('logs running specs when log_running_specs is true', function() {
trivialReporter.logRunningSpecs = true;
spyOn(trivialReporter, 'log');
trivialReporter.reportSpecStarting(spec1);
expect(trivialReporter.log).toHaveBeenCalledWith('>> Jasmine Running suite 1 spec 1...');
});
});
});

View File

@ -0,0 +1,127 @@
var fs = require('fs');
var util = require('util');
var path = require('path');
// yes, really keep this here to keep us honest, but only for jasmine's own runner! [xw]
// undefined = "diz be undefined yo";
var jasmineGlobals = require('../lib/jasmine-core/jasmine.js');
for (var k in jasmineGlobals) {
global[k] = jasmineGlobals[k];
}
require('../src/console/ConsoleReporter.js');
/*
Pulling in code from jasmine-node.
We can't just depend on jasmine-node because it has its own jasmine that it uses.
*/
global.window = {
setTimeout: setTimeout,
clearTimeout: clearTimeout,
setInterval: setInterval,
clearInterval: clearInterval
};
delete global.window;
function noop() {
}
jasmine.executeSpecs = function(specs, done, isVerbose, showColors) {
for (var i = 0, len = specs.length; i < len; ++i) {
var filename = specs[i];
require(filename.replace(/\.\w+$/, ""));
}
var jasmineEnv = jasmine.getEnv();
var consoleReporter = new jasmine.ConsoleReporter(util.print, done, showColors);
jasmineEnv.addReporter(consoleReporter);
jasmineEnv.execute();
};
jasmine.getAllSpecFiles = function(dir, matcher) {
var specs = [];
if (fs.statSync(dir).isFile() && dir.match(matcher)) {
specs.push(dir);
} else {
var files = fs.readdirSync(dir);
for (var i = 0, len = files.length; i < len; ++i) {
var filename = dir + '/' + files[i];
if (fs.statSync(filename).isFile() && filename.match(matcher)) {
specs.push(filename);
} else if (fs.statSync(filename).isDirectory()) {
var subfiles = this.getAllSpecFiles(filename, matcher);
subfiles.forEach(function(result) {
specs.push(result);
});
}
}
}
return specs;
};
function now() {
return new Date().getTime();
}
jasmine.asyncSpecWait = function() {
var wait = jasmine.asyncSpecWait;
wait.start = now();
wait.done = false;
(function innerWait() {
waits(10);
runs(function() {
if (wait.start + wait.timeout < now()) {
expect('timeout waiting for spec').toBeNull();
} else if (wait.done) {
wait.done = false;
} else {
innerWait();
}
});
})();
};
jasmine.asyncSpecWait.timeout = 4 * 1000;
jasmine.asyncSpecDone = function() {
jasmine.asyncSpecWait.done = true;
};
for (var key in jasmine) {
exports[key] = jasmine[key];
}
/*
End jasmine-node runner
*/
var isVerbose = false;
var showColors = true;
process.argv.forEach(function(arg) {
switch (arg) {
case '--color': showColors = true; break;
case '--noColor': showColors = false; break;
case '--verbose': isVerbose = true; break;
}
});
var specs = jasmine.getAllSpecFiles(__dirname, new RegExp(".js$"));
var domIndependentSpecs = [];
for (var i = 0; i < specs.length; i++) {
if (fs.readFileSync(specs[i], "utf8").indexOf("document.createElement") < 0) {
domIndependentSpecs.push(specs[i]);
}
}
jasmine.executeSpecs(domIndependentSpecs, function(runner, log) {
if (runner.results().failedCount === 0) {
process.exit(0);
} else {
process.exit(1);
}
}, isVerbose, showColors);

View File

@ -0,0 +1,86 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Jasmine Spec Runner: Jasmine Core</title>
<link rel="shortcut icon" type="image/png" href="../images/jasmine_favicon.png">
<link href="../lib/jasmine-core/jasmine.css" rel="stylesheet"/>
<script type="text/javascript" src="../lib/jasmine-core/jasmine.js"></script>
<script type="text/javascript">
// yes, really keep this here to keep us honest, but only for jasmine's own runner! [xw]
undefined = "diz be undefined yo";
</script>
<!-- include source files here... -->
<script type="text/javascript" src="../src/html/HtmlReporterHelpers.js"></script>
<script type="text/javascript" src="../src/html/HtmlReporter.js"></script>
<script type="text/javascript" src="../src/html/HtmlReporterHelpers.js"></script>
<script type="text/javascript" src="../src/html/ReporterView.js"></script>
<script type="text/javascript" src="../src/html/SpecView.js"></script>
<script type="text/javascript" src="../src/html/SuiteView.js"></script>
<script type="text/javascript" src="../src/html/TrivialReporter.js"></script>
<script type="text/javascript" src="../src/console/ConsoleReporter.js"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="core/BaseSpec.js"></script>
<script type="text/javascript" src="core/CustomMatchersSpec.js"></script>
<script type="text/javascript" src="core/EnvSpec.js"></script>
<script type="text/javascript" src="core/ExceptionsSpec.js"></script>
<script type="text/javascript" src="core/JsApiReporterSpec.js"></script>
<script type="text/javascript" src="core/MatchersSpec.js"></script>
<script type="text/javascript" src="core/MockClockSpec.js"></script>
<script type="text/javascript" src="core/MultiReporterSpec.js"></script>
<script type="text/javascript" src="core/NestedResultsSpec.js"></script>
<script type="text/javascript" src="core/PrettyPrintSpec.js"></script>
<script type="text/javascript" src="core/QueueSpec.js"></script>
<script type="text/javascript" src="core/ReporterSpec.js"></script>
<script type="text/javascript" src="core/RunnerSpec.js"></script>
<script type="text/javascript" src="core/SpecRunningSpec.js"></script>
<script type="text/javascript" src="core/SpecSpec.js"></script>
<script type="text/javascript" src="core/SpySpec.js"></script>
<script type="text/javascript" src="core/SuiteSpec.js"></script>
<script type="text/javascript" src="core/UtilSpec.js"></script>
<script type="text/javascript" src="core/WaitsForBlockSpec.js"></script>
<script type="text/javascript" src="html/HTMLReporterSpec.js"></script>
<script type="text/javascript" src="html/MatchersHtmlSpec.js"></script>
<script type="text/javascript" src="html/PrettyPrintHtmlSpec.js"></script>
<script type="text/javascript" src="html/TrivialReporterSpec.js"></script>
<script type="text/javascript" src="console/ConsoleReporterSpec.js"></script>
<script type="text/javascript">
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();
</script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,51 @@
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# Require this file using `require "spec_helper.rb"` to ensure that it is only
# loaded once.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.run_all_when_everything_filtered = true
config.filter_run :focus
end
require 'awesome_print'
require 'tmpdir'
require 'nokogiri'
def project_root
File.join(File.expand_path(File.dirname(__FILE__)), '..')
end
require "#{project_root}/tasks/jasmine_dev"
def capture_output(capture = true)
if capture
output = StringIO.new
$stdout = output
end
yield
if capture
output.string
end
ensure
$stdout = STDOUT
end
def reset_dir(dir)
FileUtils.rm_r dir if File.exists?(dir)
FileUtils.mkdir_p dir
end
def jasmine_version
version = jasmine_version_object
version_string = "#{version['major']}.#{version['minor']}.#{version['build']}"
version_string += ".rc#{version['release_candidate']}" if version['release_candidate']
version_string
end
def jasmine_version_object
@version_object ||= JSON.parse(File.read(File.join(JasmineDev.project_root, 'src', 'version.json')))
end

View File

@ -0,0 +1,33 @@
require 'spec_helper.rb'
describe "Build Jasmine task" do
let(:jasmine_core_dir) { "#{Dir.tmpdir}/jasmine-core" }
let(:jasmine_dev) { JasmineDev.new }
before do
reset_dir jasmine_core_dir
@output = capture_output { jasmine_dev.build_distribution jasmine_core_dir }
end
it "should say that JSHint is running" do
@output.should match(/Running JSHint/)
@output.should match(/Jasmine JSHint PASSED/)
end
it "should tell the developer it is building the distribution" do
@output.should match(/Building Jasmine distribution/)
end
it "should build jasmine.js in the destination directory" do
File.exist?("#{jasmine_core_dir}/jasmine.js").should be_true
end
it "should build jasmine-html.js in the destination directory" do
File.exist?("#{jasmine_core_dir}/jasmine-html.js").should be_true
end
it "should build jasmine.css" do
File.exist?("#{jasmine_core_dir}/jasmine.css").should be_true
end
end

View File

@ -0,0 +1,60 @@
require 'spec_helper.rb'
describe "Build Github Pages task" do
let(:pages_dir) { File.join(Dir.tmpdir, 'pages') }
let(:jasmine_dev) { JasmineDev.new }
before do
reset_dir pages_dir
end
describe "when the Github pages submodule is not present" do
before do
jasmine_dev.should_receive(:has_pages_submodule?).and_return(false)
@output = capture_output { jasmine_dev.build_github_pages pages_dir }
end
it "should tell the user the task is running" do
@output.should match(/Building Github Pages/)
end
it "should prompt the user to install the submodule" do
@output.should match(/Submodule for Github Pages isn't present/)
end
end
describe "when the Github pages submodule is present" do
before do
jasmine_dev.should_receive(:has_pages_submodule?).and_return(true)
@output = capture_output { jasmine_dev.build_github_pages pages_dir }
end
it "should tell the user the task is running" do
@output.should match(/Building Github Pages/)
end
it "should copy the latest jasmine files to the pages dir" do
['jasmine.js', 'jasmine.css', 'jasmine-html.js'].each do |lib_file|
source = File.read(File.join(project_root, 'lib', 'jasmine-core', lib_file))
dest = File.read(File.join(pages_dir, 'lib', lib_file))
source.should == dest
end
end
it "should build a new page" do
@output.should match(/rocco/)
File.exist?(File.join(pages_dir, 'introduction.html')).should be_true
end
it "should copy the rocco output to index.html" do
introduction = File.read(File.join(pages_dir, 'introduction.html'))
index = File.read(File.join(pages_dir, 'index.html'))
index.should == introduction
end
end
end

View File

@ -0,0 +1,109 @@
require 'spec_helper.rb'
describe "Standalone Distribution tasks" do
let(:jasmine_dev) { JasmineDev.new }
let(:standalone_temp_dir) { File.join(Dir.tmpdir, 'jasmine_test') }
let(:download_dir) { File.join(standalone_temp_dir, 'download')}
describe "build_standalone_distribution" do
before do
reset_dir standalone_temp_dir
reset_dir download_dir
Dir.should_receive(:tmpdir).any_number_of_times.and_return(standalone_temp_dir)
@standalone_staging_dir = File.join(standalone_temp_dir, 'jasmine_standalone')
@version_dir = File.join(@standalone_staging_dir, "jasmine-standalone-#{jasmine_version}")
@lib_dir = File.join(@version_dir, 'lib')
@source_dir = File.join(@version_dir, 'src')
@spec_dir = File.join(@version_dir, 'spec')
@output = capture_output { jasmine_dev.build_standalone_distribution download_dir }
end
it "should build the distribution" do
@output.should match(/Building Jasmine distribution/)
end
it "should tell the developer the task has started" do
@output.should match(/Building standalone distribution/)
end
it "should copy the lib directory to the staging directory, under a versioned directory" do
lib_dir_files = Dir.glob(File.join(standalone_temp_dir, 'jasmine_standalone', '**', '*'))
staged_lib_files = %w{ jasmine.js jasmine-html.js jasmine.css MIT.LICENSE }
staged_lib_files.each do |filename|
lib_dir_files.should include(File.join(@lib_dir, "jasmine-#{jasmine_version}", filename))
end
end
it "should copy the sample project source to the staging directory" do
File.exist?(File.join(@source_dir, 'Player.js')).should be_true
File.exist?(File.join(@source_dir, 'Song.js')).should be_true
end
it "should copy the sample project specs to the staging directory" do
File.exist?(File.join(@spec_dir, 'PlayerSpec.js')).should be_true
File.exist?(File.join(@spec_dir, 'SpecHelper.js')).should be_true
end
it "should copy a build SpecRunner.html to the staging directory" do
File.exist?(File.join(@version_dir, 'SpecRunner.html')).should be_true
end
it "should zip up the contents of the staging directory" do
File.exist?(File.join(@standalone_staging_dir, "jasmine-standalone-#{jasmine_version}.zip")).should be_true
end
it "should copy the zip file to the pages sub directory" do
File.exist?(File.join(download_dir, "jasmine-standalone-#{jasmine_version}.zip")).should be_true
end
describe "when the zip file is unzipped" do
before do
@out_directory = File.join(standalone_temp_dir, 'unzip')
reset_dir @out_directory
FileUtils.cp File.join(@standalone_staging_dir, "jasmine-standalone-#{jasmine_version}.zip"),
@out_directory
Dir.chdir @out_directory do
system("unzip -qq jasmine-standalone-#{jasmine_version}.zip")
end
end
describe "the distirbution" do
before do
Dir.chdir @out_directory do
@files = Dir.glob(File.join('**', '*'))
end
end
it "should include the correct root files" do
@files.should include('SpecRunner.html')
end
it "should include the correct lib files" do
%w{ jasmine.js jasmine-html.js jasmine.css MIT.LICENSE }.each do |file|
@files.should include(File.join('lib', "jasmine-#{jasmine_version}", file))
end
end
it "should include the correct src files" do
%w{Player.js Song.js}.each do |file|
@files.should include(File.join('src', file))
end
end
it "should include the correct spec files" do
%w{PlayerSpec.js SpecHelper.js}.each do |file|
@files.should include(File.join('spec', file))
end
end
end
end
end
end

View File

@ -0,0 +1,63 @@
require 'spec_helper.rb'
describe "Build Standalone runner HTML task" do
let(:jasmine_dev) { JasmineDev.new }
let(:standalone_temp_dir) { "#{Dir.tmpdir}/jasmine_test" }
describe "build_standalone_runner" do
before do
reset_dir standalone_temp_dir
Dir.should_receive(:tmpdir).any_number_of_times.and_return(standalone_temp_dir)
@standalone_staging_dir = File.join(standalone_temp_dir, 'jasmine_standalone')
@version_dir = File.join(@standalone_staging_dir, "jasmine-standalone-#{jasmine_version}")
@output = capture_output { jasmine_dev.build_standalone_runner }
end
it "should tell the developer the task has started" do
@output.should match(/Building standalone runner HTML/)
end
it "should copy a build SpecRunner.html to the staging directory" do
File.exist?(File.join(@version_dir, 'SpecRunner.html')).should be_true
end
describe "should build the file that has HTML that" do
before do
html = File.read(File.join(@version_dir, 'SpecRunner.html'))
@runner = Nokogiri(html)
end
it "should have the favicon tag" do
favicon_tag = @runner.css('link')[0]
favicon_tag['href'].should match("lib/jasmine-#{jasmine_version}/jasmine_favicon.png")
end
it "should have the stylesheet" do
css_tag = @runner.css('link')[1]
css_tag['href'].should match("lib/jasmine-#{jasmine_version}/jasmine.css")
end
it "should have the jasmine script tags" do
script_sources = @runner.css('script').collect {|tag| tag['src']}
script_sources.should include("lib/jasmine-#{jasmine_version}/jasmine.js")
script_sources.should include("lib/jasmine-#{jasmine_version}/jasmine-html.js")
end
it "should have the example source files" do
script_sources = @runner.css('script').collect {|tag| tag['src']}
script_sources.should include('src/Player.js')
script_sources.should include('src/Song.js')
end
it "should have the example source files" do
script_sources = @runner.css('script').collect {|tag| tag['src']}
script_sources.should include('spec/SpecHelper.js')
script_sources.should include('spec/PlayerSpec.js')
end
end
end
end

View File

@ -0,0 +1,26 @@
require 'spec_helper.rb'
describe "Spec counting task" do
let(:jasmine_dev) { JasmineDev.new }
before do
@output = capture_output { jasmine_dev.count_specs }
end
it "should tell the developer that the specs are being counted" do
@output.should match(/Counting specs/)
end
it "should report the number of specs that will run in node" do
@output.should match(/\d+ \e\[0mspecs for Node.js/)
end
it "should report the number of specs that will run in the browser" do
@output.should match(/\d+ \e\[0mspecs for Browser/)
end
it "should remind the developer to check the count" do
@output.should match(/Please verify/)
end
end

View File

@ -0,0 +1,81 @@
require 'spec_helper.rb'
describe "Spec tasks" do
let(:jasmine_dev) { JasmineDev.new }
describe "execute_specs_in_node" do
describe "when Node.js is not present" do
before do
jasmine_dev.should_receive(:has_node?).and_return(false)
@output = capture_output { jasmine_dev.execute_specs_in_node }
end
it "should prompt the user to install Node" do
@output.should match(/Node\.js is required/)
end
end
describe "when Node.js is present" do
before do
jasmine_dev.should_receive(:has_node?).and_return(true)
@output = capture_output { jasmine_dev.execute_specs_in_node }
end
it "should build the distribution" do
@output.should match(/Building Jasmine distribution/)
end
it "should tell the developer that the specs are being counted" do
@output.should match(/Counting specs/)
end
it "should tell the user that the specs are running in Node.js" do
@output.should match(/specs via Node/)
@output.should match(/Started/)
@output.should match(/\d+ specs, 0 failures/)
end
end
end
describe "execute_specs_in_browser" do
before do
jasmine_dev.should_receive(:run)
@output = capture_output { jasmine_dev.execute_specs_in_browser }
end
it "should build the distribution" do
@output.should match(/Building Jasmine distribution/)
end
it "should tell the developer that the specs are being counted" do
@output.should match(/Counting specs/)
end
it "should tell the user that the specs are running in the broswer" do
@output.should match(/specs via the default web browser/)
end
end
describe "execute_specs" do
before do
@output = capture_output { jasmine_dev.execute_specs }
end
it "should build the distribution" do
@output.should match(/Building Jasmine distribution/)
end
it "should tell the developer that the specs are being counted" do
@output.should match(/Counting specs/)
end
it "should tell the user that the specs are running in Node.js" do
@output.should match(/specs via Node/)
end
it "should tell the user that the specs are running in the broswer" do
@output.should match(/specs via the default web browser/)
end
end
end

View File

@ -0,0 +1,39 @@
require 'spec_helper.rb'
describe "JSHint task" do
let(:tmp_dir) { "#{Dir.tmpdir}/jasmine_tasks_test" }
let(:jasmine_dev) { JasmineDev.new }
before do
reset_dir tmp_dir
end
describe "when Node is not present" do
before do
jasmine_dev.should_receive(:has_node?).and_return(false)
@output = capture_output { jasmine_dev.js_hint }
end
it "should not tell the user that lint is running" do
@output.should_not match(/Running JSHint/)
end
it "should prompt the user to install Node" do
@output.should match(/Node\.js is required/)
end
end
describe "when Node is present" do
before do
jasmine_dev.should_receive(:has_node?).and_return(true)
@output = capture_output { jasmine_dev.js_hint }
end
it "should tell the user that lint is running" do
@output.should match(/Running JSHint/)
@output.should match(/Jasmine JSHint PASSED/)
end
end
end

View File

@ -0,0 +1,39 @@
require 'spec_helper.rb'
describe "Release task" do
let(:jasmine_dev) { JasmineDev.new }
describe "when the pages submodule is not present" do
before do
jasmine_dev.should_receive(:has_pages_submodule?).and_return(false)
@output = capture_output { jasmine_dev.release_prep }
end
it "should tell the user the task is running" do
@output.should match(/Building Release/)
end
it "should prompt the user to install the submodule" do
@output.should match(/Submodule for Github Pages isn't present/)
end
end
describe "when the pages submodule is present" do
before do
JasmineDev.any_instance.should_receive(:write_version_files)
JasmineDev.any_instance.should_receive(:build_distribution)
JasmineDev.any_instance.should_receive(:build_standalone_distribution)
JasmineDev.any_instance.should_receive(:build_github_pages)
jasmine_dev.should_receive(:has_pages_submodule?).and_return(true)
@output = capture_output { jasmine_dev.release_prep }
end
it "should tell the user the task is running" do
@output.should match(/Building Release/)
end
end
end

View File

@ -0,0 +1,55 @@
require 'spec_helper.rb'
describe "Version tasks" do
let(:jasmine_dev) { JasmineDev.new }
describe "write_version_files" do
before do
@output = capture_output { jasmine_dev.write_version_files }
end
it "should tell the user that the task has started" do
@output.should match(/Building version files/)
end
it "should build the version.js file" do
js_version = File.read(File.join(project_root, 'src', 'version.js'))
js_version.should match(%Q{"build": #{jasmine_version_object["build"]}})
js_version.should match(%Q{"minor": #{jasmine_version_object["minor"]}})
js_version.should match(%Q{"build": #{jasmine_version_object["build"]}})
if jasmine_version_object["release_candidate"]
js_version.should match(%Q{"release_candidate": #{jasmine_version_object["release_candidate"]}})
end
js_version.should match(/"revision": \d+/)
end
it "should build the jasmine-core ruby gem version" do
ruby_version = File.read(File.join(project_root, 'lib', 'jasmine-core', 'version.rb'))
ruby_version.should match(%Q{VERSION = "#{jasmine_version}"})
end
end
describe "display_version" do
describe "when Node.js is not present" do
before do
@output = capture_output { jasmine_dev.display_version }
end
it "should display a version header" do
@output.should match(/Current version/)
end
it "should display the current version Object" do
@output.should match(/Display version: \e\[33m\d+\.\d+\.\d+/)
end
it "should display the current version string" do
@output.should match(/\{ "major": \d+, "minor": \d+, "build": \d+/)
end
end
end
end

Some files were not shown because too many files have changed in this diff Show More