initial commit
This commit is contained in:
commit
ccd63b9393
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
*.gem
|
||||
*.rbc
|
||||
.bundle
|
||||
.config
|
||||
.yardoc
|
||||
Gemfile.lock
|
||||
InstalledFiles
|
||||
_yardoc
|
||||
coverage
|
||||
doc/
|
||||
lib/bundler/man
|
||||
pkg
|
||||
rdoc
|
||||
spec/reports
|
||||
test/tmp
|
||||
test/version_tmp
|
||||
tmp
|
4
Gemfile
Normal file
4
Gemfile
Normal file
@ -0,0 +1,4 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# Specify your gem's dependencies in jasmine-gwt.gemspec
|
||||
gemspec
|
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
Copyright (c) 2012 John Bintz
|
||||
|
||||
MIT License
|
||||
|
||||
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.
|
84
README.md
Normal file
84
README.md
Normal file
@ -0,0 +1,84 @@
|
||||
`jasmine-gwt` provides a cool Given-When-Then syntax not dissimilar to [Cucumber](http://cukes.info/), but
|
||||
running entirely in [Jasmine](http://pivotal.github.com/jasmine/). No other dependencies (except CoffeeScript,
|
||||
but that'll change.) Also, writing these in CoffeeScript gives you an experience really close to Cucumber, so yeah.
|
||||
|
||||
Import the library, say by using Sprockets in your `spec_helper.js.coffee`:
|
||||
|
||||
``` coffeescript
|
||||
#= require jasmine-gwt
|
||||
#= require_tree ./steps
|
||||
```
|
||||
|
||||
Then create some features for a thing:
|
||||
|
||||
``` coffeescript
|
||||
Feature 'Complex Backbone View', ->
|
||||
Background ->
|
||||
Given 'I have a model', ->
|
||||
@model = new Backbone.Model()
|
||||
Given 'I have a view', ->
|
||||
@view = new ComplexBackboneView(model: @model)
|
||||
|
||||
Scenario 'Basics', ->
|
||||
When 'I render the view'
|
||||
Then 'the view should return itself'
|
||||
Then 'the view should contain ".loader"'
|
||||
|
||||
Scenario 'Model has a name', ->
|
||||
Given 'the model has the name "John"'
|
||||
When 'I render the view'
|
||||
Then 'the view has ".name" with the text "John"'
|
||||
```
|
||||
|
||||
Run it and you'll be told you need to define some steps, Cucumber-style:
|
||||
|
||||
``` coffeescript
|
||||
When /I render the view/, ->
|
||||
@result = @view.render()
|
||||
|
||||
Then /the view should return itself/, ->
|
||||
expect(@result).toEqual(@view)
|
||||
|
||||
Then /the view should contain "([^"]+)"/, (selector) ->
|
||||
expect($(@view.el)).toContain(selector)
|
||||
|
||||
Given /the model has the name "([^"]+)"/, (name) ->
|
||||
@model.set(name: name)
|
||||
|
||||
Then /the view has the "([^"]+)" with the text "([^"]+)"/, (selector, text) ->
|
||||
expect(view.$(selector)).toHaveText(text)
|
||||
```
|
||||
|
||||
If you give a step a callback in the `Scenario` or `Background` block, it'll just run that code.
|
||||
|
||||
Then write your usual Jasmine specs to test from bottom-up, and use the features to test from the top-down.
|
||||
It's awesome! Errors are reported along with the rest of your Jasmine specs, since `jasmine-gwt` just generates
|
||||
`describe` and `it` blocks for each of the steps.
|
||||
|
||||
You also have `Before` and `After` hooks for each scenario. Here's a handy one for Sinon.JS fake server support.
|
||||
|
||||
```
|
||||
Before ->
|
||||
@server = sinon.fakeServer.create()
|
||||
|
||||
After ->
|
||||
@server.restore()
|
||||
|
||||
*Important*: Use single-arrows when defining steps. Each block of code is executed in the same context, and mucking
|
||||
with that would make thing probably not work too well.
|
||||
|
||||
## Why not another GWT library, or Cucumber itself?
|
||||
|
||||
I wanted something that worked as much like Cucumber as possible without actually bringing in Cucumber. Bringing in
|
||||
Cucumber into my toolchain would be more work right now than I want to deal with (making `jasmine-headless-webkit` support
|
||||
CommonJS `require`, for instance), but I also wanted as much of the cool magic that Cucumber had. Therefore, this library.
|
||||
|
||||
Todo:
|
||||
|
||||
* Test the code (I cowboyed it! Yeehaw!)
|
||||
* Step hooks (`BeforeEach` and `AfterEach`)
|
||||
* Tagged scenarios
|
||||
* Hooks that only react to certain tags
|
||||
|
||||
So alpha it's pretty much the letter before alpha.
|
||||
|
17
jasmine-gwt.gemspec
Normal file
17
jasmine-gwt.gemspec
Normal file
@ -0,0 +1,17 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
require File.expand_path('../lib/jasmine-gwt/version', __FILE__)
|
||||
|
||||
Gem::Specification.new do |gem|
|
||||
gem.authors = ["John Bintz"]
|
||||
gem.email = ["john@coswellproductions.com"]
|
||||
gem.description = %q{Given-When-Then syntax for Jasmine that looks suspiciously like Cucumber}
|
||||
gem.summary = %q{Given-When-Then syntax for Jasmine that looks suspiciously like Cucumber}
|
||||
gem.homepage = ""
|
||||
|
||||
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
||||
gem.files = `git ls-files`.split("\n")
|
||||
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
||||
gem.name = "jasmine-gwt"
|
||||
gem.require_paths = ["lib"]
|
||||
gem.version = Jasmine::GWT::VERSION
|
||||
end
|
175
lib/assets/javascripts/jasmine-gwt.js.coffee
Normal file
175
lib/assets/javascripts/jasmine-gwt.js.coffee
Normal file
@ -0,0 +1,175 @@
|
||||
jasmine.GWT =
|
||||
Step: (type, name, parameter) ->
|
||||
if scenario = jasmine.GWT.currentScenario_
|
||||
scenario[type](name, parameter)
|
||||
else
|
||||
jasmine.GWT.Steps[type] ||= []
|
||||
jasmine.GWT.Steps[type].push([ name, parameter ])
|
||||
Hook: (type, code) ->
|
||||
jasmine.GWT.Hooks[type] ||= []
|
||||
jasmine.GWT.Hooks[type].push(code)
|
||||
|
||||
runHook: (type, context) ->
|
||||
for code in (jasmine.GWT.Hooks[type] || [])
|
||||
code.apply(context)
|
||||
|
||||
Steps: {}
|
||||
Hooks: {}
|
||||
|
||||
class jasmine.GWT.Feature
|
||||
constructor: (@name, code) ->
|
||||
jasmine.GWT.currentFeature_ = this
|
||||
|
||||
@background = null
|
||||
code.apply(this)
|
||||
|
||||
jasmine.GWT.currentFeature_ = null
|
||||
|
||||
Scenario: (name, code) ->
|
||||
new jasmine.GWT.Scenario(name, code, this)
|
||||
|
||||
Background: (code) ->
|
||||
@background = new jasmine.GWT.Background(code)
|
||||
|
||||
class jasmine.GWT.Background
|
||||
constructor: (code) ->
|
||||
@statements = []
|
||||
|
||||
jasmine.GWT.currentScenario_ = this
|
||||
|
||||
code.apply(this)
|
||||
|
||||
jasmine.GWT.currentScenario_ = null
|
||||
|
||||
Given: (name, code) ->
|
||||
@statements.push([ 'Given', name, code ])
|
||||
When: (name, code) ->
|
||||
@statements.push([ 'When', name, code ])
|
||||
Then: (name, code) ->
|
||||
@statements.push([ 'Then', name, code ])
|
||||
|
||||
class jasmine.GWT.Scenario extends jasmine.GWT.Background
|
||||
constructor: (scenarioName, code, @feature) ->
|
||||
super(code)
|
||||
|
||||
_statements = this.allStatements()
|
||||
_this = this
|
||||
|
||||
describe @feature.name, ->
|
||||
it scenarioName, ->
|
||||
jasmine.GWT.runHook('Before', _this)
|
||||
|
||||
for [ type, name, param ] in _statements
|
||||
_this._type = type
|
||||
_this._name = name
|
||||
|
||||
runCode = (param, args = []) ->
|
||||
resultsIndex = jasmine.getEnv().currentSpec.results().getItems().length
|
||||
|
||||
param.apply(_this, args)
|
||||
|
||||
for index in [ resultsIndex...(jasmine.getEnv().currentSpec.results().getItems().length) ]
|
||||
result = jasmine.getEnv().currentSpec.results().getItems()[index]
|
||||
result.message = "[#{type} #{name}] " + result.message
|
||||
|
||||
args = []
|
||||
|
||||
if param?
|
||||
if typeof param == "function"
|
||||
runCode(param)
|
||||
continue
|
||||
else
|
||||
args = [ param ]
|
||||
|
||||
found = false
|
||||
|
||||
if jasmine.GWT.Steps[type]
|
||||
for [ match, code ] in jasmine.GWT.Steps[type]
|
||||
if match.constructor == RegExp
|
||||
if result = name.match(match)
|
||||
result.shift()
|
||||
runCode(code, result.concat(args))
|
||||
found = true
|
||||
|
||||
break
|
||||
else
|
||||
if name == match
|
||||
runCode(code)
|
||||
found = true
|
||||
|
||||
break
|
||||
|
||||
if !found
|
||||
output = [ "", "No step defined for #{type} #{name}. Define one using the following:", '' ]
|
||||
|
||||
argCount = args.length
|
||||
|
||||
name = name.replace /\d+/g, (match) ->
|
||||
argCount += 1
|
||||
"(\\d+)"
|
||||
|
||||
name = name.replace /\"[^"]+\"/g, (match) ->
|
||||
argCount += 1
|
||||
'"([^"]+)"'
|
||||
|
||||
if argCount == 0
|
||||
output.push("#{type} /#{name}/, ->")
|
||||
else
|
||||
args = ("arg#{i}" for i in [ 1..(argCount) ])
|
||||
|
||||
output.push("#{type} /#{name}/, (#{args.join(', ')}) ->")
|
||||
|
||||
output.push(" @not_defined()")
|
||||
output.push("")
|
||||
|
||||
fakeResult = new jasmine.ExpectationResult(
|
||||
matcherName: 'steps',
|
||||
passed: false,
|
||||
expected: 'to be defined',
|
||||
actual: "#{type} #{name}",
|
||||
message: output.join("\n")
|
||||
)
|
||||
|
||||
jasmine.getEnv().currentSpec.addMatcherResult(fakeResult)
|
||||
|
||||
jasmine.GWT.runHook('After', _this)
|
||||
|
||||
allStatements: =>
|
||||
allStatements = []
|
||||
|
||||
if @feature.background?
|
||||
allStatements = @feature.background.statements
|
||||
|
||||
allStatements.concat(@statements)
|
||||
|
||||
not_defined: =>
|
||||
fakeResult = new jasmine.ExpectationResult(
|
||||
matcherName: 'step',
|
||||
passed: false,
|
||||
expected: 'to be defined',
|
||||
actual: "#{@_type} #{@_name}",
|
||||
message: "has no code defined."
|
||||
)
|
||||
|
||||
jasmine.getEnv().currentSpec.addMatcherResult(fakeResult)
|
||||
|
||||
jasmine.GWT.globals =
|
||||
Feature: (args...) ->
|
||||
new jasmine.GWT.Feature(args...)
|
||||
|
||||
Background: (args...) ->
|
||||
jasmine.GWT.currentFeature_.Background(args...)
|
||||
|
||||
Scenario: (args...) ->
|
||||
jasmine.GWT.currentFeature_.Scenario(args...)
|
||||
|
||||
Given: (args...) -> jasmine.GWT.Step('Given', args...)
|
||||
When: (args...) -> jasmine.GWT.Step('When', args...)
|
||||
Then: (args...) -> jasmine.GWT.Step('Then', args...)
|
||||
|
||||
Before: (args...) -> jasmine.GWT.Hook('Before', args...)
|
||||
After: (args...) -> jasmine.GWT.Hook('After', args...)
|
||||
|
||||
for method, code of jasmine.GWT.globals
|
||||
(exports ? this)[method] = code
|
||||
|
7
lib/jasmine-gwt.rb
Normal file
7
lib/jasmine-gwt.rb
Normal file
@ -0,0 +1,7 @@
|
||||
require "jasmine-gwt/version"
|
||||
|
||||
module Jasmine
|
||||
module GWT
|
||||
# Your code goes here...
|
||||
end
|
||||
end
|
5
lib/jasmine-gwt/version.rb
Normal file
5
lib/jasmine-gwt/version.rb
Normal file
@ -0,0 +1,5 @@
|
||||
module Jasmine
|
||||
module GWT
|
||||
VERSION = "0.0.1"
|
||||
end
|
||||
end
|
0
spec/javascripts/jasmine-gwt_spec.js.coffee
Normal file
0
spec/javascripts/jasmine-gwt_spec.js.coffee
Normal file
0
spec/javascripts/support/jasmine.yml
Normal file
0
spec/javascripts/support/jasmine.yml
Normal file
Loading…
Reference in New Issue
Block a user