initial commit

This commit is contained in:
John Bintz 2012-01-05 10:52:23 -05:00
commit ccd63b9393
11 changed files with 333 additions and 0 deletions

17
.gitignore vendored Normal file
View 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
View File

@ -0,0 +1,4 @@
source 'https://rubygems.org'
# Specify your gem's dependencies in jasmine-gwt.gemspec
gemspec

22
LICENSE Normal file
View 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
View 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.

2
Rakefile Normal file
View File

@ -0,0 +1,2 @@
#!/usr/bin/env rake
require "bundler/gem_tasks"

17
jasmine-gwt.gemspec Normal file
View 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

View 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
View File

@ -0,0 +1,7 @@
require "jasmine-gwt/version"
module Jasmine
module GWT
# Your code goes here...
end
end

View File

@ -0,0 +1,5 @@
module Jasmine
module GWT
VERSION = "0.0.1"
end
end

View File