opal-rspec 0.0.1.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +15 -0
- data/README.md +78 -0
- data/Rakefile +46 -0
- data/app/rspec-builder.rb +10 -0
- data/app/rspec/core.rb +202 -0
- data/app/rspec/core/configuration.rb +1070 -0
- data/app/rspec/core/example_group.rb +573 -0
- data/app/rspec/core/project_initializer.rb +0 -0
- data/app/rspec/core/shared_example_group.rb +146 -0
- data/app/rspec/core/shared_example_group/collection.rb +27 -0
- data/app/rspec/matchers/built_in/have.rb +0 -0
- data/config.ru +10 -0
- data/lib/opal-rspec.rb +2 -0
- data/lib/opal/rspec.rb +6 -0
- data/lib/opal/rspec/rake_task.rb +44 -0
- data/lib/opal/rspec/version.rb +6 -0
- data/opal-rspec.gemspec +23 -0
- data/opal/opal-rspec.rb +1 -0
- data/opal/opal/rspec.rb +21 -0
- data/opal/opal/rspec/browser_formatter.rb +194 -0
- data/opal/opal/rspec/fixes.rb +62 -0
- data/opal/opal/rspec/runner.rb +52 -0
- data/opal/opal/rspec/sprockets_runner.rb.erb +11 -0
- data/opal/opal/rspec/text_formatter.rb +74 -0
- data/spec/example_spec.rb +143 -0
- data/spec/matchers_spec.rb +181 -0
- data/vendor/spec_runner.js +41 -0
- metadata +113 -0
File without changes
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Core
|
3
|
+
module SharedExampleGroup
|
4
|
+
# @overload shared_examples(name, &block)
|
5
|
+
# @overload shared_examples(name, tags, &block)
|
6
|
+
#
|
7
|
+
# Wraps the `block` in a module which can then be included in example
|
8
|
+
# groups using `include_examples`, `include_context`, or
|
9
|
+
# `it_behaves_like`.
|
10
|
+
#
|
11
|
+
# @param [String] name to match when looking up this shared group
|
12
|
+
# @param block to be eval'd in a nested example group generated by `it_behaves_like`
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
#
|
16
|
+
# shared_examples "auditable" do
|
17
|
+
# it "stores an audit record on save!" do
|
18
|
+
# lambda { auditable.save! }.should change(Audit, :count).by(1)
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# class Account do
|
23
|
+
# it_behaves_like "auditable" do
|
24
|
+
# def auditable; Account.new; end
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# @see ExampleGroup.it_behaves_like
|
29
|
+
# @see ExampleGroup.include_examples
|
30
|
+
# @see ExampleGroup.include_context
|
31
|
+
def shared_examples(*args, &block)
|
32
|
+
SharedExampleGroup.registry.add_group(self, *args, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
alias_method :shared_context, :shared_examples
|
36
|
+
alias_method :share_examples_for, :shared_examples
|
37
|
+
alias_method :shared_examples_for, :shared_examples
|
38
|
+
|
39
|
+
def shared_example_groups
|
40
|
+
SharedExampleGroup.registry.shared_example_groups_for('main', *ancestors[0..-1])
|
41
|
+
end
|
42
|
+
|
43
|
+
module TopLevelDSL
|
44
|
+
def shared_examples(*args, &block)
|
45
|
+
SharedExampleGroup.registry.add_group('main', *args, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
alias_method :shared_context, :shared_examples
|
49
|
+
alias_method :share_examples_for, :shared_examples
|
50
|
+
alias_method :shared_examples_for, :shared_examples
|
51
|
+
|
52
|
+
def shared_example_groups
|
53
|
+
SharedExampleGroup.registry.shared_example_groups_for('main')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.registry
|
58
|
+
@registry ||= Registry.new
|
59
|
+
end
|
60
|
+
|
61
|
+
# @private
|
62
|
+
#
|
63
|
+
# Used internally to manage the shared example groups and
|
64
|
+
# constants. We want to limit the number of methods we add
|
65
|
+
# to objects we don't own (main and Module) so this allows
|
66
|
+
# us to have helper methods that don't get added to those
|
67
|
+
# objects.
|
68
|
+
class Registry
|
69
|
+
def add_group(source, *args, &block)
|
70
|
+
ensure_block_has_source_location(block, CallerFilter.first_non_rspec_line)
|
71
|
+
|
72
|
+
if key? args.first
|
73
|
+
key = args.shift
|
74
|
+
warn_if_key_taken source, key, block
|
75
|
+
add_shared_example_group source, key, block
|
76
|
+
end
|
77
|
+
|
78
|
+
unless args.empty?
|
79
|
+
mod = Module.new
|
80
|
+
(class << mod; self; end).send :define_method, :extended do |host|
|
81
|
+
host.class_eval(&block)
|
82
|
+
end
|
83
|
+
RSpec.configuration.extend mod, *args
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def shared_example_groups_for(*sources)
|
88
|
+
Collection.new(sources, shared_example_groups)
|
89
|
+
end
|
90
|
+
|
91
|
+
def shared_example_groups
|
92
|
+
@shared_example_groups ||= Hash.new { |hash,key| hash[key] = Hash.new }
|
93
|
+
end
|
94
|
+
|
95
|
+
def clear
|
96
|
+
shared_example_groups.clear
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def add_shared_example_group(source, key, block)
|
102
|
+
shared_example_groups[source][key] = block
|
103
|
+
end
|
104
|
+
|
105
|
+
def key?(candidate)
|
106
|
+
[String, Symbol, Module].any? { |cls| cls === candidate }
|
107
|
+
end
|
108
|
+
|
109
|
+
def warn_if_key_taken(source, key, new_block)
|
110
|
+
return unless existing_block = example_block_for(source, key)
|
111
|
+
|
112
|
+
RSpec.warn_with <<-WARNING
|
113
|
+
WARN: Shared example group '#{key}' has been previously defined at:
|
114
|
+
WARN
|
115
|
+
WARN
|
116
|
+
WARN
|
117
|
+
WARN
|
118
|
+
WARNING
|
119
|
+
end
|
120
|
+
|
121
|
+
def formatted_location(block)
|
122
|
+
block.source_location.join ":"
|
123
|
+
end
|
124
|
+
|
125
|
+
def example_block_for(source, key)
|
126
|
+
shared_example_groups[source][key]
|
127
|
+
end
|
128
|
+
|
129
|
+
if Proc.method_defined?(:source_location)
|
130
|
+
def ensure_block_has_source_location(block, caller_line); end
|
131
|
+
else # for 1.8.7
|
132
|
+
def ensure_block_has_source_location(block, caller_line)
|
133
|
+
block.extend Module.new {
|
134
|
+
define_method :source_location do
|
135
|
+
caller_line.split(':')
|
136
|
+
end
|
137
|
+
}
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
extend RSpec::Core::SharedExampleGroup::TopLevelDSL
|
146
|
+
Module.send(:include, RSpec::Core::SharedExampleGroup::TopLevelDSL)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Core
|
3
|
+
module SharedExampleGroup
|
4
|
+
class Collection
|
5
|
+
|
6
|
+
def initialize(sources, examples)
|
7
|
+
@sources, @examples = sources, examples
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](key)
|
11
|
+
fetch_examples(key)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def fetch_examples(key)
|
17
|
+
@examples[source_for(key)][key]
|
18
|
+
end
|
19
|
+
|
20
|
+
def source_for(key)
|
21
|
+
@sources.reverse.find { |source| @examples[source].has_key? key }
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
File without changes
|
data/config.ru
ADDED
data/lib/opal-rspec.rb
ADDED
data/lib/opal/rspec.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'opal/rspec'
|
2
|
+
require 'opal-sprockets'
|
3
|
+
|
4
|
+
module Opal
|
5
|
+
module RSpec
|
6
|
+
class RakeTask
|
7
|
+
include Rake::DSL if defined? Rake::DSL
|
8
|
+
|
9
|
+
RUNNER = File.expand_path('../../../../vendor/spec_runner.js', __FILE__)
|
10
|
+
PORT = 9999
|
11
|
+
URL = "http://localhost:9999/"
|
12
|
+
|
13
|
+
def initialize(name = 'opal:rspec', &block)
|
14
|
+
desc "Run opal specs in phantomjs"
|
15
|
+
task name do
|
16
|
+
require 'rack'
|
17
|
+
require 'webrick'
|
18
|
+
|
19
|
+
server = fork do
|
20
|
+
app = Opal::Server.new { |s|
|
21
|
+
s.main = 'opal/rspec/sprockets_runner'
|
22
|
+
s.append_path 'spec'
|
23
|
+
s.debug = false
|
24
|
+
|
25
|
+
block.call s if block
|
26
|
+
}
|
27
|
+
|
28
|
+
Rack::Server.start(:app => app, :Port => PORT, :AccessLog => [],
|
29
|
+
:Logger => WEBrick::Log.new("/dev/null"))
|
30
|
+
end
|
31
|
+
|
32
|
+
system "phantomjs #{RUNNER} \"#{URL}\""
|
33
|
+
success = $?.success?
|
34
|
+
|
35
|
+
Process.kill(:SIGINT, server)
|
36
|
+
Process.wait
|
37
|
+
|
38
|
+
exit 1 unless success
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
data/opal-rspec.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/opal/rspec/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'opal-rspec'
|
6
|
+
s.version = Opal::RSpec::VERSION
|
7
|
+
s.author = 'Adam Beynon'
|
8
|
+
s.email = 'adam.beynon@gmail.com'
|
9
|
+
s.homepage = 'http://opalrb.org'
|
10
|
+
s.summary = 'RSpec for Opal'
|
11
|
+
s.description = 'Opal compatible rspec library'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.files << 'opal/opal/rspec/rspec.js'
|
15
|
+
|
16
|
+
s.require_paths = ['lib']
|
17
|
+
|
18
|
+
s.add_dependency 'opal', '~> 0.4.4'
|
19
|
+
s.add_dependency 'opal-sprockets', '~> 0.2.0'
|
20
|
+
|
21
|
+
s.add_development_dependency 'rake'
|
22
|
+
end
|
23
|
+
|
data/opal/opal-rspec.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'opal/rspec'
|
data/opal/opal/rspec.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'file'
|
2
|
+
require 'dir'
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
# vendor a pre-built rspec
|
6
|
+
require 'opal/rspec/rspec'
|
7
|
+
|
8
|
+
require 'opal/rspec/fixes'
|
9
|
+
require 'opal/rspec/text_formatter'
|
10
|
+
require 'opal/rspec/browser_formatter'
|
11
|
+
require 'opal/rspec/runner'
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
# For now, always use our custom formatter for results
|
15
|
+
config.formatter = Opal::RSpec::Runner.default_formatter
|
16
|
+
|
17
|
+
# Always support expect() and .should syntax (we should not do this really..)
|
18
|
+
config.expect_with :rspec do |c|
|
19
|
+
c.syntax = [:should, :expect]
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
module Opal
|
2
|
+
module RSpec
|
3
|
+
class BrowserFormatter < ::RSpec::Core::Formatters::BaseFormatter
|
4
|
+
|
5
|
+
def start(example_count)
|
6
|
+
super
|
7
|
+
|
8
|
+
@summary_element = Element.new(:p, class_name: 'summary', text: 'Runner...')
|
9
|
+
@groups_element = Element.new(:ul, class_name: 'example_groups')
|
10
|
+
|
11
|
+
target = Element.new(`document.body`)
|
12
|
+
target << @summary_element
|
13
|
+
target << @groups_element
|
14
|
+
|
15
|
+
styles = Element.new(:style, type: 'text/css', css_text: CSS_STYLES)
|
16
|
+
styles.append_to_head
|
17
|
+
end
|
18
|
+
|
19
|
+
def example_group_started(example_group)
|
20
|
+
super
|
21
|
+
|
22
|
+
@example_group_failed = false
|
23
|
+
@group_element = Element.new(:li, class_name: 'group passed')
|
24
|
+
|
25
|
+
description = Element.new(:span, class_name: 'group_description', text: example_group.description)
|
26
|
+
@group_element << description
|
27
|
+
|
28
|
+
@example_list = Element.new(:ul, class_name: 'examples')
|
29
|
+
@group_element << @example_list
|
30
|
+
|
31
|
+
@groups_element << @group_element
|
32
|
+
end
|
33
|
+
|
34
|
+
def example_group_finished(example_group)
|
35
|
+
super
|
36
|
+
|
37
|
+
if @example_group_failed
|
38
|
+
@group_element.class_name = 'group failed'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def example_failed(example)
|
43
|
+
super
|
44
|
+
|
45
|
+
@example_group_failed = true
|
46
|
+
|
47
|
+
error = example.execution_result[:exception]
|
48
|
+
error_name = error.class.name.to_s
|
49
|
+
output = "#{short_padding}#{error_name}:\n"
|
50
|
+
error.message.to_s.split("\n").each { |line| output += "#{long_padding} #{line}\n" }
|
51
|
+
|
52
|
+
wrapper = Element.new(:li, class_name: 'example failed')
|
53
|
+
|
54
|
+
description = Element.new(:span, class_name: 'example_description', text: example.description)
|
55
|
+
wrapper << description
|
56
|
+
|
57
|
+
exception = Element.new(:pre, class_name: 'exception', text: output)
|
58
|
+
wrapper << exception
|
59
|
+
|
60
|
+
@example_list << wrapper
|
61
|
+
@example_list.style :display, 'list-item'
|
62
|
+
end
|
63
|
+
|
64
|
+
def example_passed(example)
|
65
|
+
super
|
66
|
+
|
67
|
+
wrapper = Element.new(:li, class_name: 'example passed')
|
68
|
+
description = Element.new(:span, class_name: 'example_description', text: example.description)
|
69
|
+
|
70
|
+
wrapper << description
|
71
|
+
@example_list << wrapper
|
72
|
+
end
|
73
|
+
|
74
|
+
def dump_summary(duration, example_count, failure_count, pending_count)
|
75
|
+
super
|
76
|
+
|
77
|
+
summary = "\n#{example_count} examples, #{failure_count} failures (time taken: #{duration})"
|
78
|
+
@summary_element.text = summary
|
79
|
+
end
|
80
|
+
|
81
|
+
def short_padding
|
82
|
+
' '
|
83
|
+
end
|
84
|
+
|
85
|
+
def long_padding
|
86
|
+
' '
|
87
|
+
end
|
88
|
+
|
89
|
+
class Element
|
90
|
+
attr_reader :native
|
91
|
+
|
92
|
+
def initialize(el, attrs={})
|
93
|
+
if String === el
|
94
|
+
@native = `document.createElement(el)`
|
95
|
+
else
|
96
|
+
@native = el
|
97
|
+
end
|
98
|
+
|
99
|
+
attrs.each { |name, val| __send__ "#{name}=", val }
|
100
|
+
end
|
101
|
+
|
102
|
+
def class_name=(name)
|
103
|
+
`#@native.className = #{name}`
|
104
|
+
end
|
105
|
+
|
106
|
+
def html=(html)
|
107
|
+
`#@native.innerHTML = #{html}`
|
108
|
+
end
|
109
|
+
|
110
|
+
def text=(text)
|
111
|
+
self.html = text.gsub(/</, '<').gsub(/>/, '>')
|
112
|
+
end
|
113
|
+
|
114
|
+
def type=(type)
|
115
|
+
`#@native.type = #{type}`
|
116
|
+
end
|
117
|
+
|
118
|
+
def append(child)
|
119
|
+
`#@native.appendChild(#{child.native})`
|
120
|
+
end
|
121
|
+
|
122
|
+
alias << append
|
123
|
+
|
124
|
+
def css_text=(text)
|
125
|
+
%x{
|
126
|
+
if (#@native.styleSheet) {
|
127
|
+
#@native.styleSheet.cssText = #{text};
|
128
|
+
}
|
129
|
+
else {
|
130
|
+
#@native.appendChild(document.createTextNode(#{text}));
|
131
|
+
}
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
def style(name, value)
|
136
|
+
`#@native.style[#{name}] = value`
|
137
|
+
end
|
138
|
+
|
139
|
+
def append_to_head
|
140
|
+
`document.getElementsByTagName('head')[0].appendChild(#@native)`
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
CSS_STYLES = <<-EOF
|
145
|
+
body {
|
146
|
+
font-size: 14px;
|
147
|
+
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
|
148
|
+
}
|
149
|
+
|
150
|
+
pre {
|
151
|
+
font-family: "Bitstream Vera Sans Mono", Monaco, "Lucida Console", monospace;
|
152
|
+
font-size: 12px;
|
153
|
+
color: #444444;
|
154
|
+
white-space: pre;
|
155
|
+
padding: 3px 0px 3px 12px;
|
156
|
+
margin: 0px 0px 8px;
|
157
|
+
|
158
|
+
background: #FAFAFA;
|
159
|
+
-webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
|
160
|
+
-webkit-border-radius: 3px;
|
161
|
+
-moz-border-radius: 3px;
|
162
|
+
border-radius: 3px;
|
163
|
+
border: 1px solid #DDDDDD;
|
164
|
+
}
|
165
|
+
|
166
|
+
ul.example_groups {
|
167
|
+
list-style-type: none;
|
168
|
+
}
|
169
|
+
|
170
|
+
li.group.passed .group_description {
|
171
|
+
color: #597800;
|
172
|
+
font-weight: bold;
|
173
|
+
}
|
174
|
+
|
175
|
+
li.group.failed .group_description {
|
176
|
+
color: #FF000E;
|
177
|
+
font-weight: bold;
|
178
|
+
}
|
179
|
+
|
180
|
+
li.example.passed {
|
181
|
+
color: #597800;
|
182
|
+
}
|
183
|
+
|
184
|
+
li.example.failed {
|
185
|
+
color: #FF000E;
|
186
|
+
}
|
187
|
+
|
188
|
+
.examples {
|
189
|
+
list-style-type: none;
|
190
|
+
}
|
191
|
+
EOF
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|