mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-09-07 10:11:17 +02:00
MINOR Updated jQuery.concrete references to point to the new "entwine" name git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@102695 467b73ca-7a2a-4603-9d3b-597d59a354a9
826 lines
27 KiB
Plaintext
826 lines
27 KiB
Plaintext
|
|
= JSpec
|
|
|
|
JSpec is a minimalistic JavaScript behavior driven development framework,
|
|
providing simple installation, extremely low learning curve, absolutely no pollution
|
|
to core prototypes, async request support, and incredibly sexy syntax, tons of matchers
|
|
and much more.
|
|
|
|
== Features
|
|
|
|
* Highly readable
|
|
* Framework / DOM independent
|
|
* Modular via JSpec Module's and hooks
|
|
* Mock Ajax Requests
|
|
* Rhino support
|
|
* Node.js support
|
|
* Async support
|
|
* Fixture support
|
|
* Ruby JavaScript testing server
|
|
* Nested describes
|
|
* Does not pollute core object prototypes
|
|
* Cascading before/after/before_each/after_each hooks
|
|
* Extremely simple and intuitive matcher declaration
|
|
* Over 45 core matchers
|
|
* Allows parens to be optional when using matchers to increase readability
|
|
* Several helpful formatters (DOM, Console, Terminal, ...)
|
|
* Assertion graphs displaying how many, and which assertions pass or failed
|
|
* Default / customizable evaluation contexts
|
|
* DOM sandbox support
|
|
* Great looking default DOM theme
|
|
* `jspec` command-line utility for auto-running specs, and initializing project templates
|
|
* Proxy or 'Spy' assertions
|
|
* Method Stubbing
|
|
* Shared behaviors
|
|
* Profiling
|
|
* Interactive Shell
|
|
* Ruby on Rails Integration
|
|
* Tiny (15 kb compressed, 1300-ish LOC)
|
|
|
|
== Installation
|
|
|
|
Simply download JSpec and include JSpec.css and JSpec.js in your markup.
|
|
Head over to the downloads section on Github, clone this public repo, or
|
|
add JSpec as a git submodule with in your project. Alternatively JSpec is
|
|
also available as a Ruby Gem (though this is not required), which also
|
|
provides the `jspec` executable. First install [Gemcutter](http://gemcutter.org/) then execute:
|
|
$ sudo gem install jspec
|
|
|
|
At which point you may:
|
|
$ jspec init myproject
|
|
|
|
By default, the command above will use absolute path for all JSpec library files.
|
|
This behavior can be a problem when you're working across different computers or
|
|
operating systems. You can freeze the library or symlink it.
|
|
|
|
$ jspec init myproject --freeze
|
|
$ jspec init myproject --symlink
|
|
|
|
JSpec scripts should NOT be referenced via the <script> tag, they should be
|
|
loaded using the exec method (unless you are using the grammar-less alternative).
|
|
Below is an example:
|
|
|
|
...
|
|
<script>
|
|
function runSuites() {
|
|
JSpec
|
|
.exec('spec.core.js')
|
|
.exec('spec.jquery.js')
|
|
.run({ failuresOnly : true })
|
|
.report()
|
|
}
|
|
</script>
|
|
<body onLoad="runSuites()">
|
|
...
|
|
|
|
You may optionally want to use sources in the /pkg directory
|
|
for your project, since it includes compressed alternatives generated
|
|
each release.
|
|
|
|
== Example
|
|
|
|
describe 'ShoppingCart'
|
|
before_each
|
|
cart = new ShoppingCart
|
|
end
|
|
|
|
describe 'addProducts'
|
|
it 'should add several products'
|
|
cart.addProduct('cookie')
|
|
cart.addProduct('icecream')
|
|
cart.should.have 2, 'products'
|
|
end
|
|
end
|
|
|
|
describe 'checkout'
|
|
it 'should throw an error when checking out with no products'
|
|
-{ cart.clear().checkout() }.should.throw_error EmptyCart
|
|
end
|
|
end
|
|
end
|
|
|
|
== Grammar-less Example
|
|
|
|
JSpec's grammar is optional, you may also use the equivalent grammar-less
|
|
alternative below using pure JavaScript (when using the JSpec grammar you
|
|
may also use grammar-less assertions):
|
|
|
|
JSpec.describe('ShoppingCart', function(){
|
|
before_each(function{
|
|
cart = new ShoppingCart
|
|
})
|
|
|
|
describe('addProducts', function(){
|
|
it ('should add several products', function(){
|
|
cart.addProducts('cookie')
|
|
cart.addProducts('icecream')
|
|
expect(cart).to(have, 2, 'products')
|
|
})
|
|
})
|
|
|
|
describe('checkout', function(){
|
|
it ('should throw an error when checking out with no products', function(){
|
|
expect(function(){ cart.clear().checkout() }).to(throw_error, EmptyCart)
|
|
})
|
|
})
|
|
})
|
|
|
|
== Options
|
|
|
|
You may alter the way JSpec operates by assigning options via the
|
|
JSpec.options hash, by passing string-based option values via the
|
|
query string, or passing a hash to run(). For example
|
|
JSpec.options.failuresOnly = true, and ?failuresOnly=1 will both work.
|
|
|
|
* profile {bool} when enabled, uses console.time() in order to display performance information in your console log as specs are completed. (DOM, Console)
|
|
* failuresOnly {bool} displays only failing specs, making them quick to discover and fix (DOM, Terminal, Server)
|
|
* reportToId {string} an element id to report to when using the DOM formatter (DOM)
|
|
* verbose {bool} verbose server output, defaults to false (Server)
|
|
|
|
== Matchers
|
|
|
|
* Core
|
|
|
|
- equal, be ===
|
|
- be_a, be_an have constructor of x
|
|
- be_an_instance_of instanceof x
|
|
- be_at_least >=
|
|
- be_at_most <=
|
|
- be_null == null
|
|
- be_empty length < 0 or {}
|
|
- be_true == true
|
|
- be_false == false
|
|
- be_type be type of x
|
|
- be_greater_than >
|
|
- be_less_than <
|
|
- be_undefined check if variable passed is undefined
|
|
- throw_error should throw an error, optionally supply the error string or regexp for message comparison
|
|
- have object should have n of property (person.should.have(2, 'pets'))
|
|
- have_at_least object should have at least n of property
|
|
- have_at_most object should have a maximum n of property
|
|
- have_within object should have within n..n of property (person.should.have_within(1..3, 'pets')
|
|
- have_length length of n
|
|
- have_prop object should have property x, optionally supplying an expected value
|
|
- have_property strict version of have_prop
|
|
- be_within checks if n is within the range passed
|
|
- include include substring, array element, or hash key
|
|
- match string should match regexp x
|
|
- respond_to property x should be a function
|
|
- eql matches simple literals (strings, numbers) with ==
|
|
However composites like arrays or 'hashes' are recursively matched,
|
|
meaning that [1, 2, [3]].should_eql([1, 2, [3]]) will be true.
|
|
|
|
* jQuery
|
|
|
|
- have_tag, have_one have exactly one tag
|
|
- have_tags, have_many have more than one tag
|
|
- have_child have exactly one child
|
|
- have_children have more than one child
|
|
- have_text have plain text
|
|
- have_attr have an attribute, with optional value
|
|
- have_type
|
|
- have_id
|
|
- have_title
|
|
- have_alt
|
|
- have_href
|
|
- have_rel
|
|
- have_rev
|
|
- have_name
|
|
- have_target
|
|
- have_value
|
|
- have_class
|
|
- have_classes
|
|
- be_visible
|
|
- be_hidden
|
|
- be_enabled
|
|
- be_disabled
|
|
- be_selected
|
|
- be_checked
|
|
|
|
== Async Support With Mock Timers
|
|
|
|
The javascript mock timers library is available at http://github.com/visionmedia/js-mock-timers
|
|
although it is already bundled with JSpec at lib/jspec.timers.js
|
|
|
|
Timers return ids and may be passed to clearInterval(), however
|
|
they do not execute in threads, they must be manually scheduled and
|
|
controlled via the tick() function.
|
|
|
|
setTimeout(function(){
|
|
alert('Wahoo!')
|
|
}, 400)
|
|
|
|
tick(200) // Nothing happens
|
|
tick(400) // Wahoo!
|
|
|
|
setInterval() works as expected, although it persists, where as setTimeout()
|
|
is destroyed after a single call. As conveyed by the last tick() call below,
|
|
a large increment in milliseconds may cause the callbacks to be called several times
|
|
to 'catch up'.
|
|
|
|
progress = ''
|
|
var id = setInterval(function(){
|
|
progress += '.'
|
|
}, 100)
|
|
|
|
tick(50), print(progress) // ''
|
|
tick(50), print(progress) // '.'
|
|
tick(100), print(progress) // '..'
|
|
tick(100), print(progress) // '...'
|
|
tick(300), print(progress) // '......'
|
|
|
|
clearInterval(id)
|
|
|
|
tick(800) // Nothing happens
|
|
|
|
You may also reset at any time using resetTimers()
|
|
|
|
== Proxy Assertions
|
|
|
|
Proxy or 'Spy' assertions allow you to assert that a method is called n number
|
|
of times, with x arguments, returning x value. For example:
|
|
|
|
person = { getPets : function(species){ return ['izzy'] }}
|
|
person.should.receive('getPets', 'twice').with_args(an_instance_of(String))and_return(['izzy'])
|
|
person.getPets('dog') // This will pass
|
|
person.getPets() // This will fail because we asked an instance of String
|
|
|
|
This is a useful mechanism for testing the behavior of your object, as well as
|
|
how other methods may interact with it. Below is another example:
|
|
|
|
array = ['foo', 'bar']
|
|
array.should.receive('toString').and_return('foo,bar')
|
|
'array: ' + array // This line causes the spec to pass due to calling toString()
|
|
|
|
For more examples view spec/spec.matchers.js
|
|
|
|
== Method Stubbing
|
|
|
|
JSpec currently provides very simple stubbing support shown below:
|
|
|
|
person = { toString : function(){ return '<Person>' } }
|
|
stub(person, 'toString').and_return('Ive been stubbed!')
|
|
|
|
After each spec all stubs are restored to their original methods so
|
|
there is no reason to explicitly call destub(). To persist stubs,
|
|
use a before_each hook:
|
|
|
|
before_each
|
|
stub(someObject, 'method').and_return({ some : thing })
|
|
end
|
|
|
|
To destub a method simply call destub() at any time:
|
|
|
|
destub(person, 'toString')
|
|
|
|
If you would like to whipe an object clear of stubs simply pass it
|
|
to destub() without an additional method argument:
|
|
|
|
destub(person)
|
|
|
|
Alternatively both these utility functions may be called as methods
|
|
on any object when using the JSpec grammar:
|
|
|
|
someObject.stub('method').and_return('whatever')
|
|
// Converted to stub(someObject, 'method').and_return('whatever')
|
|
|
|
== Helpers
|
|
|
|
* Core
|
|
|
|
- an_instance_of used in conjunction with the 'receive' matcher
|
|
- mockRequest, mock_request mock a request (requires jspec.xhr.js)
|
|
- unmockRequest, unmock_request unmock requests (requests jspec.xhr.js)
|
|
|
|
* jQuery
|
|
|
|
- sandbox used to generate new DOM sandbox, using jQuery object
|
|
- element same as invoking jQuery, just reads better and no need to worry about $ collisions
|
|
- elements alias of element
|
|
|
|
== Shared Behaviors
|
|
|
|
JSpec's support for shared behaviors allows multiple suites or describe blocks to share
|
|
common functionality. For example an Admin, would inherit all specs of User:
|
|
|
|
describe 'User'
|
|
before
|
|
User = function(name) { this.name = name }
|
|
user = new User('joe')
|
|
end
|
|
|
|
it 'should have a name'
|
|
user.should.have_property 'name'
|
|
end
|
|
|
|
describe 'Administrator'
|
|
should_behave_like('User')
|
|
|
|
before
|
|
Admin = function(name) { this.name = name }
|
|
Admin.prototype.may = function(perm){ return true }
|
|
user = new Admin('tj')
|
|
end
|
|
|
|
it 'should have access to all permissions'
|
|
user.may('edit pages').should.be_true
|
|
end
|
|
end
|
|
end
|
|
|
|
NOTE: both User and Administrator's before hooks implement the 'user' variable
|
|
|
|
== Mock Ajax Requests
|
|
|
|
JSpec supports generic Ajax mocking which is usable with any JavaScript framework via 'jspec.xhr.js'. The
|
|
API is comprised of two functions, mockRequest() and unmockRequest(). unmockRequest() is
|
|
automatically called after each specification to restore the default functionality of XMLHttpRequest,
|
|
so it is uncommon to call unmockRequest() directly. Below is a jQuery example:
|
|
|
|
it 'should mock requests'
|
|
mockRequest().and_return('{ foo : "bar" }', 'application/json')
|
|
$.getJSON('foo', function(response, statusText){
|
|
response.foo.should.eql 'bar'
|
|
})
|
|
end
|
|
|
|
The mockRequest().and_return signature is as follows:
|
|
|
|
mockRequest().and_return(<data>, [content-type], [response-status-code], [headers-hash])
|
|
|
|
At the moment mockRequest() itself does not accept any arguments, however in the future
|
|
this will be used to target specific uris for mocking.
|
|
|
|
NOTE: works with Rhino as well
|
|
|
|
== Hooks
|
|
|
|
Currently the following hooks are supported, and may be utilized any number of times as they
|
|
are simply pushed to a stack. So for instance you may have two before_each blocks within the same
|
|
scope, they will both run, but this can help keep your specs readable.
|
|
|
|
* before run once before the suite is executed
|
|
* after run once after the suite is executed
|
|
* before_each run before each specification
|
|
* after_each run after each specification
|
|
|
|
== Custom Contexts
|
|
|
|
Custom contexts can be applied to supply helper
|
|
methods or properties to all subsequent bodies (other hooks, or specs).
|
|
|
|
Keep in mind that when replacing the default context you will loose
|
|
functionality provided by it, unless you manually merge it with your
|
|
custom context.
|
|
|
|
To reset the context simply assign null to obtain the original context.
|
|
|
|
...
|
|
before
|
|
JSpec.context = { foo : 'bar' }
|
|
end
|
|
|
|
after
|
|
JSpec.context = null
|
|
end
|
|
|
|
it 'will work ;)'
|
|
foo.should_equal 'bar'
|
|
end
|
|
...
|
|
|
|
== Async Support
|
|
|
|
Currently only jspec.jquery.js supports async requests. JSpec uses jQuery.ajaxSetup and sets all
|
|
requests to sync, which preserves execution order, and reports correctly.
|
|
|
|
it 'should load mah cookies (textfile)'
|
|
$.post('async', function(text){
|
|
text.should_eql 'cookies!'
|
|
})
|
|
end
|
|
|
|
== Pre-processor
|
|
|
|
The pre-processing capability of JSpec is extremely powerful. Your JavaScript
|
|
code is not necessarily what it seems. For example when you seemingly invoke a
|
|
object's prototype like below:
|
|
|
|
'foobar'.should.include 'bar'
|
|
|
|
First parens are added:
|
|
|
|
'foobar'.should.include('bar')
|
|
|
|
Secondly the matcher invocation is converted to a non-polluting match() call:
|
|
|
|
JSpec.match('foobar', 'should', 'include', 'bar')
|
|
|
|
This also means instead of:
|
|
|
|
var object = { foo : 'bar' }
|
|
object.should.include 'foo'
|
|
|
|
We can do:
|
|
|
|
{ foo : 'bar' }.should.include 'foo'
|
|
|
|
=== Closure Literal
|
|
|
|
These are equivalent:
|
|
|
|
-{ throw 'test' }.should.throw_error
|
|
function() { throw 'test' }.should.throw_error
|
|
|
|
=== Inclusive Range Literal
|
|
|
|
The following expands to the array of [1,2,3,4,5]
|
|
|
|
n.should.be_within 1..5
|
|
|
|
== Formatters
|
|
|
|
To change a formatter simply alter the options hash like below, assigning
|
|
a new constructor, or pass it within the hash to run():
|
|
|
|
JSpec.options.formatter = JSpec.formatters.Console
|
|
|
|
OR
|
|
|
|
JSpec
|
|
.exec('...')
|
|
.run({ formatter : JSpec.formatters.Terminal })
|
|
.report()
|
|
|
|
== Fixtures
|
|
|
|
The fixture() utility function may be used in order to load arbitrary file contents
|
|
for use with your specifications. JSpec will resolve fixture('data') in the following
|
|
manor:
|
|
|
|
- 'data'
|
|
- 'spec/data'
|
|
- 'spec/fixtures/data'
|
|
- 'spec/fixtures/data.html'
|
|
|
|
So if the file 'spec/fixtures/data.html' exists, we can simply use fixture('data'),
|
|
where as 'spec/fixtures/xml/data.xml' must be specified with fixture('xml/data.xml').
|
|
|
|
If you prefer not to store fixtures in the 'fixtures' directory you must be more specific
|
|
with the path supplied.
|
|
|
|
== Testing DOM Elements
|
|
|
|
When using jQuery testing DOM elements is very easy. Many may think they require specific
|
|
sandbox divs in their html, however you do not. Using the fixture support mentioned above
|
|
you may simply load some HTML, and use the 'elements()' utility which is an alias of jQuery:
|
|
|
|
describe 'JSpec DOM testing'
|
|
describe 'is so easy'
|
|
before_each
|
|
list = elements(fixture('users-list'))
|
|
// or list = jQuery(fixture('users-list'))
|
|
// or list = $(fixture('users-list'))
|
|
end
|
|
|
|
it 'should have users'
|
|
list.should.have_tag 'ul'
|
|
end
|
|
end
|
|
end
|
|
|
|
You may also use simple strings, since jQuery's constructor will convert them to DOM elements:
|
|
|
|
describe 'Something'
|
|
before_each
|
|
html = elements('<p>Foo</p>')
|
|
// or html = $('<p>Foo</p>') ...
|
|
end
|
|
|
|
it 'should do something'
|
|
html.should.have_text 'Foo'
|
|
end
|
|
end
|
|
|
|
== Custom Matchers
|
|
|
|
First lets create a simple equality matcher. In the case below JSpec is smart enough to realize
|
|
this is simply a binary operator, and simply transforms this into 'actual === expected'
|
|
|
|
JSpec.addMatchers({
|
|
equal : '==='
|
|
})
|
|
|
|
To alias a method to keep your specs readable you may alias them like below:
|
|
|
|
JSpec.addMatchers({
|
|
be : 'alias equal'
|
|
})
|
|
|
|
'foo'.should.equal 'foo'
|
|
true.should.be true
|
|
|
|
Matchers with string bodies implicitly return the expression value.
|
|
The expanded version of the equal matcher would then be:
|
|
|
|
JSpec.addMatchers({
|
|
equal : 'actual === expected'
|
|
})
|
|
|
|
Large matchers or those which require several parameters may wish
|
|
to utilize the hash method:
|
|
|
|
JSpec.addMatchers({
|
|
equal : { match : function(actual, expected){
|
|
return actual === expected
|
|
}}
|
|
})
|
|
|
|
To keep JSpec tiny, JSpec will default to generating failure messages
|
|
for you, how ever this can be explicitly defined:
|
|
|
|
JSpec.addMatchers({
|
|
equal : {
|
|
match : function(actual, expected){
|
|
return actual === expected
|
|
},
|
|
message : function(actual, expected, negate) {
|
|
return 'a message here'
|
|
}
|
|
}
|
|
})
|
|
|
|
When defining matchers that are extremely similar in functionality, however
|
|
require different names, you may use a prefixed list of words like below which
|
|
defines be_disabled, be_selected, be_checked, and have_type, have_id, etc. Each
|
|
function must return the matcher body which will be used.
|
|
|
|
JSpec.addMatchers({
|
|
'be disabled selected checked' : function(attr) {
|
|
return 'jQuery(actual).attr("' + attr + '")'
|
|
},
|
|
|
|
'have type id title alt href src sel rev name target' : function(attr) {
|
|
return function(actual, value) {
|
|
return value ? jQuery(actual).attr(attr) == value:
|
|
jQuery(actual).attr(attr)
|
|
}
|
|
}
|
|
})
|
|
|
|
== Extending Or Hooking Into JSpec
|
|
|
|
JSpec provides a hook architecture for extending or analyzing various
|
|
points in its execution, through the use of 'Modules'. For a Module
|
|
example view lib/jspec.jquery.js.
|
|
|
|
The following methods or properties are utilized by JSpec:
|
|
|
|
- name : module name string
|
|
- init : called to initialize a module
|
|
- formatters : hash of formatters merged with JSpec.formatters
|
|
- utilities : hash of utility functions merged with JSpec.defaultContext
|
|
- matchers : hash of matchers merged with JSpec's core matchers via JSpec.addMatchers()
|
|
- DSLs : hash of DSL methods; for example DSLs.snake contains before_each, after_each, etc.
|
|
Where as DSLs.camel may contain beforeEach, afterEach, etc.
|
|
|
|
Below is a list of hooks, descriptions, and valid return values which
|
|
may simply be implemented as module methods. beforeSuite, afterSuite, beforeSpec, and afterSpec have lower
|
|
precedence than before_each, after_each etc within the specs themselves, allowing them to override or undo
|
|
anything that has been done by a Module.
|
|
|
|
- running(options) : started running JSpec with the options passed : returning 'stop' will halt running
|
|
- loading(file) : loading a file : returning 'stop' will prevent loading
|
|
- executing(file) : executing a file : returning 'stop' will prevent execution
|
|
- posting(data, url) : posting data to a url : returning 'stop' will prevent request
|
|
- preprocessing(input) : before input string is preprocessed : return input string for next hook to preprocess
|
|
- stubbing(object, method, result) : called when stubbing an object's method, and return value (result). : (no return value)
|
|
- requiring(dependency, message) : requiring a dependency : (no return value)
|
|
- beforeAssertion(assertion) : before an assertion has been made : (no return value)
|
|
- afterAssertion(assertion) : after an assertion has been made : (no return value)
|
|
- addingMatcher(name, body) : unprocessed matcher name and body : (no return value)
|
|
- addingSuite(suite) : adding Suite instance to JSpec : (no return value)
|
|
- beforeSuite(suite) : before running of suite (describe block) : (no return value)
|
|
- afterSuite(suite) : after running of suite (describe block) : (no return value)
|
|
- beforeSpec(spec) : before running of spec (it block) : (no return value)
|
|
- afterSpec(spec) : after running of spec (it block) : (no return value)
|
|
- reporting(options) : called before reporting : (no return value)
|
|
- evaluatingBody(dsl, matchers, context, contents) : evaluating body contents, with the given context, matchers and dsl. : (no return value)
|
|
|
|
For example you may wish to proxy files which are being executed, simply implement the
|
|
executing method like below. This example will stop execution of any file matching /matchers/.
|
|
|
|
MyModule = {
|
|
executing : function(file) {
|
|
if (file.match(/matchers/))
|
|
return 'stop'
|
|
}
|
|
}
|
|
JSpec.include(MyModule)
|
|
|
|
Immutable values may also be passed to hooks using hookImmutable() internally. This allows
|
|
for simple numbers, strings, etc to be utilized or altered within a hook implementation. Below
|
|
is an example module which adds functionality to the JSpec grammar by converting SomeObject.stub('method')
|
|
to stub(SomeObject, 'method'):
|
|
|
|
JSpec.include({
|
|
preprocessing : function(input) {
|
|
return input.replace(/(\w+)\.(stub|destub)\((.*?)\)$/gm, '$2($1, $3)')
|
|
}
|
|
})
|
|
|
|
== JSpec Command-line Utility
|
|
|
|
When installed as a Ruby Gem, the `jspec` executable will become available,
|
|
allowing you to initialize project templates quickly, as well as auto-testing
|
|
specifications when a file is altered.
|
|
|
|
Initialize JSpec-driven project template in directory 'myproject':
|
|
$ jspec init myproject
|
|
|
|
Once within 'myproject' start testing by executing:
|
|
$ jspec
|
|
|
|
For additional usage execute:
|
|
$ jspec help
|
|
|
|
Or for specific usage:
|
|
$ jspec help run
|
|
|
|
== Rhino
|
|
|
|
JSpec provides transparent support for Rhino, while using the Terminal formatter.
|
|
Simply create a JavaScript file with contents similar to below, and then execute
|
|
the command following it:
|
|
|
|
load('lib/jspec.js')
|
|
|
|
JSpec
|
|
.exec('spec/spec.grammar.js')
|
|
.exec('spec/spec.core.js')
|
|
.run({ formatter : JSpec.formatters.Terminal, failuresOnly : true })
|
|
.report()
|
|
|
|
Initialize project with:
|
|
$ jspec init myproject
|
|
|
|
Run with:
|
|
$ jspec run --rhino
|
|
|
|
Or bind (automated testing):
|
|
$ jspec --rhino
|
|
|
|
== Server
|
|
|
|
The Ruby JavaScript testing server included with JSpec simply runs
|
|
the spec suites within each browser you specify, while reporting result
|
|
back to the terminal. It is essentially the same as using the DOM formatter
|
|
and auto-testing each browser, however results are centralized to the terminal,
|
|
removing the need to manually view each browser's output.
|
|
|
|
When utilizing the server if a file named spec/jspec.rb (or jspec/jspec.rb for rails)
|
|
is present, then it will be loaded before the server is started. This allows you to
|
|
add Sinatra routes, support additional Browsers, etc.
|
|
|
|
Run with all supported browsers:
|
|
$ jspec run --server
|
|
|
|
Run with specific browsers:
|
|
$ jspec run --browsers Safari,Firefox,Chrome,Explorer
|
|
|
|
Run with alternative browser names:
|
|
$ jspec run --browsers safari,ff,chrome,ie
|
|
|
|
Browsers supported in core:
|
|
Browser::Safari
|
|
Browser::Chrome
|
|
Browser::Opera
|
|
Browser::Firefox
|
|
Browser::IE
|
|
|
|
Supplied routes:
|
|
/slow/NUMBER
|
|
/status/NUMBER
|
|
|
|
For example $.get('/slow/4', function(){}) will take 4 seconds
|
|
to reply, where as $.get('/status/404', function(){}) will respond
|
|
with an 404 status code. Add additional Sinatra routes to the jspec.rb
|
|
file to add your own functionality.
|
|
|
|
== Interactive Shell
|
|
|
|
JSpec provides an interactive shell through Rhino, utilize with:
|
|
|
|
$ jspec shell
|
|
|
|
Or to specify additional files to load:
|
|
|
|
$ jspec shell lib/*.js
|
|
|
|
Or view additional shell help
|
|
|
|
$ jspec help shell
|
|
|
|
== Ruby on Rails
|
|
|
|
No additional gems are required for JSpec to work with rails, although
|
|
http://github.com/bhauman/jspec-rails has been created by 'bhauman'. JSpec
|
|
supports Rails out of the box, simply execute:
|
|
|
|
$ jspec init --rails
|
|
|
|
Then while still in the root directory of your Rails project, run the following
|
|
command which will bind to, and refresh your browsers automatically when any changes
|
|
are made to ./public/javascripts/*.js or ./jspec/*.js
|
|
|
|
$ jspec
|
|
|
|
Or just like regular JSpec applications, run once:
|
|
|
|
$ jspec run
|
|
|
|
Or run via the terminal using Rhino:
|
|
|
|
$ jspec run --rhino
|
|
|
|
== Support Browsers
|
|
|
|
Browsers below are supported and can be found in server/browsers.rb, however
|
|
your spec/server.rb file may support additional browsers.
|
|
|
|
* Safari
|
|
* Chrome
|
|
* Firefox
|
|
* Opera
|
|
* Internet Explorer
|
|
|
|
== Known Issues
|
|
|
|
* Tabs may cause a parse error. To prevent this use 'soft tabs' (setting in your IDE/Editor)
|
|
or use JSpec's grammar-less alternative (mentioned above).
|
|
|
|
* The preprocessor is not (yet) capable of multiline conversions. For example the following is invalid
|
|
|
|
object.stub('getContentsOfURL').and_return(function(url){
|
|
return 'html'
|
|
})
|
|
|
|
In cases such as this, you may always revert to utilizing JSpec in a grammar-less form as follows:
|
|
|
|
stub(object, 'getContentsOfURL').and_return(function(url){
|
|
return 'html'
|
|
})
|
|
|
|
== Additional JSpec Modules
|
|
|
|
* JSocka stubbing http://github.com/gisikw/jsocka/tree/master
|
|
|
|
== More Information
|
|
|
|
* IRC Channel irc://irc.freenode.net#jspec
|
|
* Featured article in JSMag: http://www.jsmag.com/main.issues.description/id=21/
|
|
* Syntax comparison with other frameworks http://gist.github.com/92283
|
|
* Get the TextMate bundle at https://github.com/visionmedia/jspec.tmbundle/tree
|
|
* For more information consult the JSpec source code documentation or visit http://visionmedia.github.com/jspec
|
|
* jQuery + HTML fixture example http://gist.github.com/147831
|
|
|
|
== Contributors
|
|
|
|
Many ideas and bug reports were contributed by
|
|
the following developers, thankyou for making
|
|
JSpec more enjoyable, and bug free ;)
|
|
|
|
* Lawrence Pit
|
|
* mpd@jesters-court.ne
|
|
* kevin.gisi@gmail.com
|
|
* tony_t_tubbs@yahoo.com
|
|
* enno84@gmx.net
|
|
* fnando
|
|
|
|
== License
|
|
|
|
(The MIT License)
|
|
|
|
Copyright (c) 2008 - 2009 TJ Holowaychuk <tj@vision-media.ca>
|
|
|
|
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.
|
|
|
|
|