opal-rspec 0.5.0.beta3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +17 -2
- data/CHANGELOG.md +4 -2
- data/README.md +38 -26
- data/Rakefile +14 -1
- data/config.ru +10 -7
- data/lib/opal/rspec/cached_environment.rb +3 -1
- data/lib/opal/rspec/post_rack_locator.rb +1 -1
- data/lib/opal/rspec/pre_rack_locator.rb +13 -49
- data/lib/opal/rspec/rake_task.rb +4 -9
- data/lib/opal/rspec/sprockets_environment.rb +5 -3
- data/lib/opal/rspec/version.rb +1 -1
- data/opal/opal/rspec/formatter/element.rb +14 -2
- data/opal/opal/rspec/formatter/html_printer.rb +14 -2
- data/spec/mri/unit/opal/rspec/cached_environment_spec.rb +16 -34
- data/spec/mri/unit/opal/rspec/rake_task_spec.rb +21 -5
- data/spec/mri/unit/opal/rspec/sprockets_environment_spec.rb +21 -60
- data/spec/rspec/core/core_spec_loader.rb +4 -0
- data/spec/rspec/expectations/expectation_spec_loader.rb +4 -0
- data/spec/rspec/mocks/filter/bugs/any_instance.rb +3 -3
- data/spec/rspec/mocks/mocks_spec_loader.rb +4 -0
- data/spec/rspec/opal_rspec_spec_loader.rb +20 -10
- data/spec/rspec/support/support_spec_loader.rb +4 -0
- data/vendor/spec_runner.js +2 -2
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1da9042a8eae6f23afb1f989cd88ac73bb16bf2b
|
4
|
+
data.tar.gz: 199ade21ce80c64e09f5123ecadd37d156c7cde9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07fd2261509f2ac82c45866d6acbce58db865a75e08fb04ae48ab8047589781b70b940f1f00b419b07ae35118a692ddb0af27e3ea9a5089a01cb06dc514359a9
|
7
|
+
data.tar.gz: fa16038e70c8daa6cd27ac081a295fdba6c9bb3f96e353e72446173cce572eca39945d3a0782cd23c04f852807fd2b435f8f6d33b09fdf3ba9334308b03d0283
|
data/.travis.yml
CHANGED
@@ -8,14 +8,29 @@ matrix:
|
|
8
8
|
fast_finish: true
|
9
9
|
|
10
10
|
include:
|
11
|
-
- rvm: 1.9.3
|
12
|
-
|
11
|
+
# - rvm: 1.9.3
|
12
|
+
# env: RUN=default
|
13
13
|
- rvm: 2.0.0
|
14
14
|
env: RUN=default
|
15
15
|
- rvm: 2.1.0
|
16
16
|
env: RUN=default
|
17
17
|
- rvm: 2.2.3
|
18
18
|
env: RUN=default
|
19
|
+
- rvm: 2.2.3
|
20
|
+
# PHANTOMJS env variable is not used but it makes it easier to differentiate this build in Travis
|
21
|
+
# https://github.com/travis-ci/travis-ci/issues/3225 - Easiest way to use 2.0 on Travis
|
22
|
+
env: RUN=default PHANTOMJS=2.0
|
23
|
+
before_script:
|
24
|
+
- "mkdir phantom20"
|
25
|
+
- "export PATH=`pwd`/phantom20:$PATH"
|
26
|
+
- "echo $PATH"
|
27
|
+
- "pushd ."
|
28
|
+
- "cd phantom20"
|
29
|
+
- "curl -L -O https://s3.amazonaws.com/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2"
|
30
|
+
- "tar xjvf phantomjs-2.0.0-ubuntu-12.04.tar.bz2"
|
31
|
+
- "popd"
|
32
|
+
- "export DISPLAY=:99.0"
|
33
|
+
- "sh -e /etc/init.d/xvfb start"
|
19
34
|
- rvm: 2.2.3
|
20
35
|
env: RUN=default RUNNER=node
|
21
36
|
- rvm: 2.2.3
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
## 0.5.0 (
|
1
|
+
## 0.5.0 (2015-12-08)
|
2
2
|
|
3
3
|
* By default, any subject, it example block, before(:each), after(:each), and around that returns a promise will be executed asynchronously. Async is NOT yet supported for context level hooks. Async approach from < 0.4.3 will still work.
|
4
4
|
|
@@ -17,7 +17,7 @@
|
|
17
17
|
* Formatters:
|
18
18
|
* Fixed issues with RSpec's BaseTextFormatter and made ProgressFormatter the default when run via the Rake task
|
19
19
|
* Fix redundant messages with expectation fails
|
20
|
-
* Browser formatter now works w/ progress bar
|
20
|
+
* Browser formatter now works w/ progress bar and has a 'Dump to console' link that will put a clickable stack trace for a failed example in the browser console
|
21
21
|
* JSON formatter supported
|
22
22
|
|
23
23
|
* Fixed issues with constants/example group naming
|
@@ -26,6 +26,8 @@
|
|
26
26
|
|
27
27
|
* A lot more matchers enabled
|
28
28
|
|
29
|
+
* PhantomJS 2.0 compatibility (also still compatible with 1.9.8). Thanks to @aost. Closes out https://github.com/opal/opal-rspec/issues/42
|
30
|
+
|
29
31
|
|
30
32
|
## 0.4.3 (2015-06-14)
|
31
33
|
|
data/README.md
CHANGED
@@ -14,6 +14,12 @@ Add `opal-rspec` to your Gemfile:
|
|
14
14
|
gem 'opal-rspec'
|
15
15
|
```
|
16
16
|
|
17
|
+
## Requirements
|
18
|
+
|
19
|
+
Besides what's already reflected in the GEM dependencies:
|
20
|
+
* PhantomJS 1.9.8 or 2.0 for Rake task testing
|
21
|
+
* Browser if you want to run and debug tests that way
|
22
|
+
|
17
23
|
### Run specs in phantomjs
|
18
24
|
|
19
25
|
To run specs, a rake task can be added which will load all spec files from
|
@@ -59,10 +65,13 @@ You can also customize the pattern of specs used similiar to how RSpec's rake ta
|
|
59
65
|
Opal::RSpec::RakeTask.new(:default) do |server, task|
|
60
66
|
# server is an instance of Opal::Server in case you want to add to the load path, customize, etc.
|
61
67
|
task.pattern = 'spec_alternate/**/*_spec.rb' # can also supply an array of patterns
|
68
|
+
# NOTE: opal-rspec, like rspec, only adds 'spec' to the Opal load path unless you set default_path
|
69
|
+
task.default_path = 'spec_alternate'
|
62
70
|
end
|
63
71
|
```
|
64
72
|
|
65
73
|
Excluding patterns can be setup this way:
|
74
|
+
|
66
75
|
```ruby
|
67
76
|
Opal::RSpec::RakeTask.new(:default) do |server, task|
|
68
77
|
task.exclude_pattern = 'spec_alternate/**/*_spec.rb' # can also supply an array of patterns
|
@@ -73,7 +82,7 @@ FileLists (as in Rake FileLists) can also be supplied:
|
|
73
82
|
|
74
83
|
```ruby
|
75
84
|
Opal::RSpec::RakeTask.new(:default) do |server, task|
|
76
|
-
task.files = FileList['spec/**/something_spec.rb]
|
85
|
+
task.files = FileList['spec/**/something_spec.rb']
|
77
86
|
end
|
78
87
|
```
|
79
88
|
|
@@ -81,14 +90,14 @@ PhantomJS will timeout by default after 60 seconds. If you need to lengthen the
|
|
81
90
|
|
82
91
|
```ruby
|
83
92
|
Opal::RSpec::RakeTask.new(:default) do |server, task|
|
84
|
-
task.files = FileList['spec/**/something_spec.rb]
|
93
|
+
task.files = FileList['spec/**/something_spec.rb']
|
85
94
|
task.timeout = 80000 # 80 seconds, unit needs to be milliseconds
|
86
95
|
end
|
87
96
|
```
|
88
97
|
|
89
98
|
### Run specs in nodejs
|
90
99
|
|
91
|
-
Same options as above, you can use the RUNNER=node environment variable or use the Rake task like so:
|
100
|
+
Same options as above, you can use the `RUNNER=node` environment variable or use the Rake task like so:
|
92
101
|
|
93
102
|
```ruby
|
94
103
|
Opal::RSpec::RakeTask.new(:default) do |server, task|
|
@@ -117,6 +126,10 @@ run Opal::Server.new(sprockets: sprockets_env) { |s|
|
|
117
126
|
Then run the rack server `bundle exec rackup` and visit `http://localhost:9292`
|
118
127
|
in any web browser.
|
119
128
|
|
129
|
+
A new feature of opal-rspec 0.5 allows you to click a 'Console' button in the browser's test results and get a
|
130
|
+
clickable stack trace in the browser console. This should ease debugging with long, concatenated script files and trying
|
131
|
+
to navigate to where an exception occurred.
|
132
|
+
|
120
133
|
## Async examples
|
121
134
|
|
122
135
|
`opal-rspec` adds support for async specs to rspec. These specs can be defined using 2 approaches:
|
@@ -180,9 +193,11 @@ end
|
|
180
193
|
```
|
181
194
|
|
182
195
|
Advantages:
|
196
|
+
|
183
197
|
* Assuming your subject under test (or matchers) return/use promises, the syntax is the same for sync or async specs
|
184
198
|
|
185
199
|
Limitations (apply to both async approaches):
|
200
|
+
|
186
201
|
* Right now, async `before(:context)` and `after(:context)` hooks cannot be async
|
187
202
|
* You cannot use an around hooks on any example where before(:each)/after(:each) hooks are async or with an async implicit subject
|
188
203
|
* `let` dependencies cannot be async, only subject
|
@@ -209,43 +224,40 @@ so it can move on. Any failures/expectations run inside this block will be run
|
|
209
224
|
in the context of the example.
|
210
225
|
|
211
226
|
Advantages:
|
227
|
+
|
212
228
|
* Hides promises from the specs
|
213
229
|
|
214
230
|
Disadvantages:
|
231
|
+
|
215
232
|
* Requires different syntax for async specs vs. sync specs
|
216
233
|
|
217
234
|
## Opal load path
|
218
235
|
|
219
|
-
NOTE: Only the
|
220
|
-
|
221
|
-
Example 1: For the example patterns above, only 'spec_alternate' will be added.
|
236
|
+
NOTE: Only the 'spec' directory will be added to the Opal load path by default. Use the Rake task's 'default_path' setting to change that
|
222
237
|
|
223
|
-
|
238
|
+
## Other Limitations/Known Issues
|
224
239
|
|
225
|
-
|
240
|
+
* Core Examples
|
241
|
+
* Example groups included like this are currently not working:
|
226
242
|
```ruby
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
'
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
243
|
+
module TestMod
|
244
|
+
def self.included(base)
|
245
|
+
base.class_eval do
|
246
|
+
describe 'foo' do
|
247
|
+
...
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
235
252
|
|
236
|
-
|
237
|
-
|
238
|
-
|
253
|
+
RSpec.configure do |c|
|
254
|
+
c.include TestMod
|
255
|
+
end
|
239
256
|
```
|
240
|
-
|
241
|
-
Only 'spec' will be added to the load path.
|
242
|
-
|
243
|
-
## Other Limitations/Known Issues
|
244
|
-
|
245
257
|
* Formatting
|
246
258
|
* Backtrace info on specs is buggy ([no Kernel::caller method in Opal](https://github.com/opal/opal/issues/894)), in Firefox w/ the browser runner, no backtraces show up with failed specs
|
247
259
|
* Diffs are not yet available when objects do not meet expectations (diff-lcs gem dependency has not been dealt with yet in Opal)
|
248
|
-
* Configuration
|
260
|
+
* Configuration
|
249
261
|
* Not all RSpec runner options are supported yet
|
250
262
|
* At some point, using node + Phantom's ability to read environment variables could be combined with a opal friendly optparse implementation to allow full options to be supplied/parsed
|
251
263
|
* Expect and should syntax are both enabled. They cannot be disabled due to bugs with the `undef` keyword in Opal 0.8. Status of changing this via config has not been tested in Opal 0.9.
|
@@ -263,7 +275,7 @@ Only 'spec' will be added to the load path.
|
|
263
275
|
* arity checking is not enabled by default in Opal but it can be. It's not currently enabled because it broke a lot of Opal specs. As a result, any matcher use (particularly respond_to) that depends on arity checking will not work
|
264
276
|
* splat methods and expect(...).to respond_to will be inconsistent in Opal 0.8 since several splat bugs were fixed with Opal 0.9
|
265
277
|
* Mocks
|
266
|
-
* `allow_any_instance/any_instance_of/any_instance`
|
278
|
+
* `allow_any_instance/any_instance_of/any_instance` are unstable and may cause runner to crash due to issues with redefining the `===` operator, which breaks a case statement inside `Hooks#find_hook`
|
267
279
|
* using expect/allow on `String`, `Number`, or any immutable bridged/native class, does not work since rspec-mocks uses singleton classes and those cannot be defined on immutable objects
|
268
280
|
* mocking class methods (including `::new`) is currently broken
|
269
281
|
* `class_double/class_spy` are not supported (it depends on `ClassVerifyingDouble` inheriting from `Module` to support transferring nested constants, but that doesn't work on Opal)
|
data/Rakefile
CHANGED
@@ -9,11 +9,22 @@ require_relative 'spec/rspec/expectations/expectation_spec_loader'
|
|
9
9
|
require_relative 'spec/rspec/support/support_spec_loader'
|
10
10
|
require_relative 'spec/rspec/mocks/mocks_spec_loader'
|
11
11
|
|
12
|
-
task :default => [:
|
12
|
+
task :default => [:phantom_node_ver,
|
13
|
+
:unit_specs,
|
14
|
+
:verify_opal_specs,
|
15
|
+
:integration_specs,
|
16
|
+
:verify_rspec_specs]
|
17
|
+
|
18
|
+
task :phantom_node_ver do
|
19
|
+
sh 'phantomjs -v'
|
20
|
+
sh 'node -v'
|
21
|
+
sh 'firefox --version'
|
22
|
+
end
|
13
23
|
|
14
24
|
desc 'Runs a set of specs in opal'
|
15
25
|
Opal::RSpec::RakeTask.new(:opal_specs) do |_, task|
|
16
26
|
task.pattern = 'spec/opal/**/*_spec.{rb,opal}'
|
27
|
+
task.default_path = 'spec/opal'
|
17
28
|
end
|
18
29
|
|
19
30
|
desc 'Generates an RSpec requires file free of dynamic requires'
|
@@ -36,10 +47,12 @@ end
|
|
36
47
|
desc 'A more limited spec suite to test pattern usage'
|
37
48
|
Opal::RSpec::RakeTask.new(:other_specs) do |_, task|
|
38
49
|
task.pattern = 'spec/other/dummy_spec.rb'
|
50
|
+
task.default_path = 'spec/other'
|
39
51
|
end
|
40
52
|
|
41
53
|
Opal::RSpec::RakeTask.new(:color_on_by_default) do |_, task|
|
42
54
|
task.pattern = 'spec/other/color_on_by_default_spec.rb'
|
55
|
+
task.default_path = 'spec/other'
|
43
56
|
end
|
44
57
|
|
45
58
|
Opal::RSpec::CoreSpecLoader.rake_tasks_for(:rspec_core_specs)
|
data/config.ru
CHANGED
@@ -2,11 +2,14 @@ require 'opal/rspec'
|
|
2
2
|
|
3
3
|
Opal::Processor.source_map_enabled = false
|
4
4
|
|
5
|
-
sprockets_env = Opal::RSpec::SprocketsEnvironment.new(spec_pattern='spec/opal/**/*_spec.{rb,opal}'
|
5
|
+
sprockets_env = Opal::RSpec::SprocketsEnvironment.new(spec_pattern='spec/opal/**/*_spec.{rb,opal}',
|
6
|
+
spec_exclude_pattern=nil,
|
7
|
+
spec_files=nil,
|
8
|
+
default_path='spec/opal')
|
6
9
|
run Opal::Server.new(sprockets: sprockets_env) { |s|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
}
|
10
|
+
s.main = 'sprockets_runner_js_errors'
|
11
|
+
# sprockets_runner_js_errors will not be in the opal load path by default
|
12
|
+
s.append_path 'spec/mri/integration/rack'
|
13
|
+
sprockets_env.add_spec_paths_to_sprockets
|
14
|
+
s.debug = ENV['OPAL_DEBUG']
|
15
|
+
}
|
@@ -13,7 +13,9 @@ module Opal
|
|
13
13
|
|
14
14
|
def get_opal_spec_requires
|
15
15
|
@locator.get_opal_spec_requires.map do |file|
|
16
|
-
|
16
|
+
asset = find_asset(file)
|
17
|
+
raise "Unable to find asset for file #{file} within load paths #{paths}. Check your load path/file specification." unless asset
|
18
|
+
logical_path = asset.logical_path
|
17
19
|
# These will go directly into require '...' statements in Opal, so need to trim extensions
|
18
20
|
logical_path.sub File.extname(logical_path), ''
|
19
21
|
end
|
@@ -1,67 +1,31 @@
|
|
1
1
|
require 'pathname'
|
2
|
+
# require the bundled RSpec's file and don't rely on the load path in case opal-rspec is included in a project's
|
3
|
+
# Gemfile without rspec also being in the Gemfile
|
4
|
+
require_relative '../../../rspec-core/lib/rspec/core/ruby_project'
|
2
5
|
|
3
6
|
module Opal
|
4
7
|
module RSpec
|
5
8
|
class PreRackLocator
|
9
|
+
include ::RSpec::Core::RubyProject
|
10
|
+
|
6
11
|
DEFAULT_PATTERN = 'spec/**/*_spec.{rb,opal}'
|
12
|
+
DEFAULT_DEFAULT_PATH = 'spec'
|
7
13
|
|
8
|
-
attr_accessor :spec_pattern, :spec_exclude_pattern, :spec_files
|
14
|
+
attr_accessor :spec_pattern, :spec_exclude_pattern, :spec_files, :default_path
|
9
15
|
|
10
|
-
def initialize(spec_pattern=nil, spec_exclude_pattern=nil, spec_files=nil)
|
16
|
+
def initialize(spec_pattern=nil, spec_exclude_pattern=nil, spec_files=nil, default_path=nil)
|
11
17
|
@spec_pattern = spec_pattern || DEFAULT_PATTERN
|
12
18
|
@spec_exclude_pattern = spec_exclude_pattern
|
13
19
|
@spec_files = spec_files
|
20
|
+
@default_path = default_path || DEFAULT_DEFAULT_PATH
|
14
21
|
end
|
15
22
|
|
16
|
-
def
|
17
|
-
|
18
|
-
# Want to get the smallest # of load paths that's common between our patterns
|
19
|
-
array_or_single = base_paths.inject do |path1, path2|
|
20
|
-
with_common_paths_replaced path1, path2
|
21
|
-
end
|
22
|
-
[*array_or_single]
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def strip_globs_from_patterns
|
28
|
-
# only using spec_pattern here since we only need paths for inclusion
|
29
|
-
[*spec_pattern].map do |each_pattern|
|
30
|
-
glob_portion = /[\*\?\[\{].*/.match each_pattern
|
31
|
-
path = glob_portion ? each_pattern.sub(glob_portion.to_s, '') : each_pattern
|
32
|
-
raise "Unable to identify a single root directory/file in the pattern #{each_pattern}. Please adjust glob" unless File.exist?(path)
|
33
|
-
path = Pathname.new path
|
34
|
-
# in case a filename was used as a pattern
|
35
|
-
path.directory? ? path : path.dirname
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def get_files_directories
|
40
|
-
spec_files.map do |file|
|
41
|
-
Pathname.new(file).dirname
|
42
|
-
end.uniq
|
23
|
+
def determine_root
|
24
|
+
find_first_parent_containing(@default_path) || '.'
|
43
25
|
end
|
44
26
|
|
45
|
-
def
|
46
|
-
|
47
|
-
replaced = [*existing_paths].map do |path|
|
48
|
-
match = nil
|
49
|
-
path.ascend do |each_level|
|
50
|
-
new_path.ascend do |each_other_level|
|
51
|
-
match = each_level if each_level.expand_path == each_other_level.expand_path
|
52
|
-
break if match
|
53
|
-
end
|
54
|
-
break if match
|
55
|
-
end
|
56
|
-
if match
|
57
|
-
new_path_covered = true
|
58
|
-
match
|
59
|
-
else
|
60
|
-
path
|
61
|
-
end
|
62
|
-
end
|
63
|
-
replaced << new_path unless new_path_covered
|
64
|
-
replaced.uniq
|
27
|
+
def get_spec_load_paths
|
28
|
+
[@default_path].map { |dir| File.join(root, dir) }
|
65
29
|
end
|
66
30
|
end
|
67
31
|
end
|
data/lib/opal/rspec/rake_task.rb
CHANGED
@@ -11,7 +11,7 @@ module Opal
|
|
11
11
|
PORT = 9999
|
12
12
|
URL = "http://localhost:#{PORT}/"
|
13
13
|
|
14
|
-
attr_accessor :pattern, :exclude_pattern, :files, :runner, :timeout
|
14
|
+
attr_accessor :pattern, :exclude_pattern, :files, :default_path, :runner, :timeout
|
15
15
|
|
16
16
|
def launch_phantom(timeout_value)
|
17
17
|
command_line = %Q{phantomjs #{RUNNER} "#{URL}"#{timeout_value ? " #{timeout_value}" : ''}}
|
@@ -93,6 +93,7 @@ module Opal
|
|
93
93
|
sprockets_env.spec_pattern = self.pattern if self.pattern
|
94
94
|
sprockets_env.spec_exclude_pattern = self.exclude_pattern
|
95
95
|
sprockets_env.spec_files = self.files
|
96
|
+
sprockets_env.default_path = self.default_path if self.default_path
|
96
97
|
raise 'Cannot supply both a pattern and files!' if self.files and self.pattern
|
97
98
|
sprockets_env.add_spec_paths_to_sprockets
|
98
99
|
}
|
@@ -112,14 +113,8 @@ module Opal
|
|
112
113
|
wait_for_server
|
113
114
|
is_phantom = runner == :phantom
|
114
115
|
if is_phantom
|
115
|
-
if `phantomjs -v`.
|
116
|
-
warn
|
117
|
-
Only PhantomJS v1 is currently supported,
|
118
|
-
if you're using homebrew on OSX you can switch version with:
|
119
|
-
|
120
|
-
brew switch phantomjs 1.9.8
|
121
|
-
|
122
|
-
WARN
|
116
|
+
if `phantomjs -v`.nil?
|
117
|
+
warn "Could not find phantomjs command"
|
123
118
|
exit 1
|
124
119
|
end
|
125
120
|
end
|
@@ -16,10 +16,12 @@ module Opal
|
|
16
16
|
:spec_exclude_pattern=,
|
17
17
|
:spec_exclude_pattern,
|
18
18
|
:spec_files=,
|
19
|
-
:spec_files
|
19
|
+
:spec_files,
|
20
|
+
:default_path=,
|
21
|
+
:default_path
|
20
22
|
|
21
|
-
def initialize(spec_pattern=nil, spec_exclude_pattern=nil, spec_files=nil)
|
22
|
-
@locator = RSpec::PreRackLocator.new spec_pattern, spec_exclude_pattern, spec_files
|
23
|
+
def initialize(spec_pattern=nil, spec_exclude_pattern=nil, spec_files=nil, default_path=nil)
|
24
|
+
@locator = RSpec::PreRackLocator.new spec_pattern, spec_exclude_pattern, spec_files, default_path
|
23
25
|
super()
|
24
26
|
end
|
25
27
|
|
data/lib/opal/rspec/version.rb
CHANGED
@@ -31,8 +31,16 @@ module Opal
|
|
31
31
|
`#@native.className`
|
32
32
|
end
|
33
33
|
|
34
|
-
def get_child_by_tag_name(tag)
|
35
|
-
|
34
|
+
def get_child_by_tag_name(tag, index=0)
|
35
|
+
elements = `#@native.getElementsByTagName(#{tag})`
|
36
|
+
# is an HTMLCollection, not an array
|
37
|
+
element_array = []
|
38
|
+
%x{
|
39
|
+
for (var i=0; i < #{elements}.length; i++) {
|
40
|
+
#{element_array}.push(#{elements}[i]);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
Element.new(element_array[index])
|
36
44
|
end
|
37
45
|
|
38
46
|
def class_name=(name)
|
@@ -47,6 +55,10 @@ module Opal
|
|
47
55
|
`#@native.outerHTML`
|
48
56
|
end
|
49
57
|
|
58
|
+
def on_click=(lambda)
|
59
|
+
`#@native.onclick = #{lambda}`
|
60
|
+
end
|
61
|
+
|
50
62
|
def html=(html)
|
51
63
|
`#@native.innerHTML = #{html}`
|
52
64
|
end
|
@@ -24,8 +24,12 @@ module Opal
|
|
24
24
|
@root_node = Element.klass 'results'
|
25
25
|
end
|
26
26
|
|
27
|
+
def current_node
|
28
|
+
@group_stack.last ? @group_stack.last : @root_node
|
29
|
+
end
|
30
|
+
|
27
31
|
def flush_output
|
28
|
-
node =
|
32
|
+
node = current_node
|
29
33
|
new_node = Element.from_string(@output.string)
|
30
34
|
node.append new_node
|
31
35
|
reset_output
|
@@ -38,7 +42,7 @@ module Opal
|
|
38
42
|
def print_example_group_start(group_id, description, number_of_parents)
|
39
43
|
super
|
40
44
|
@output.puts '</dl></div>'
|
41
|
-
parent_node =
|
45
|
+
parent_node = current_node
|
42
46
|
new_node = Element.from_string(@output.string)
|
43
47
|
reset_output
|
44
48
|
parent_node << new_node
|
@@ -60,6 +64,14 @@ module Opal
|
|
60
64
|
def print_example_failed(pending_fixed, description, run_time, failure_id, exception, extra_content, escape_backtrace=false)
|
61
65
|
super
|
62
66
|
flush_output
|
67
|
+
example_we_just_wrote = current_node.get_child_by_tag_name('dd', index=-1)
|
68
|
+
dump_message = lambda do
|
69
|
+
puts "Exception for example '#{description}'\n#{exception[:backtrace]}"
|
70
|
+
false
|
71
|
+
end
|
72
|
+
button = Element.from_string('<form><button type="button">Console</button></form>')
|
73
|
+
button.on_click = dump_message
|
74
|
+
example_we_just_wrote << button
|
63
75
|
end
|
64
76
|
|
65
77
|
def print_example_pending(description, pending_message)
|
@@ -6,10 +6,11 @@ require_relative 'temp_dir_helper'
|
|
6
6
|
describe Opal::RSpec::CachedEnvironment do
|
7
7
|
let(:pattern) { nil }
|
8
8
|
let(:exclude_pattern) { nil }
|
9
|
+
let(:default_path) { nil }
|
9
10
|
let(:files) { nil }
|
10
11
|
include_context :temp_dir
|
11
12
|
|
12
|
-
let(:original_env) { Opal::RSpec::SprocketsEnvironment.new pattern, exclude_pattern, files }
|
13
|
+
let(:original_env) { Opal::RSpec::SprocketsEnvironment.new pattern, exclude_pattern, files, default_path }
|
13
14
|
|
14
15
|
subject(:env) do
|
15
16
|
# in subject to allow contexts to execute before logic
|
@@ -20,54 +21,35 @@ describe Opal::RSpec::CachedEnvironment do
|
|
20
21
|
describe '#get_opal_spec_requires' do
|
21
22
|
subject { env.get_opal_spec_requires.sort }
|
22
23
|
|
23
|
-
context '
|
24
|
+
context 'no default path set' do
|
24
25
|
before do
|
25
26
|
create_dummy_spec_files 'spec/foobar/dummy_spec.rb', 'spec/foobar/ignored_spec.opal'
|
26
27
|
end
|
27
28
|
|
28
29
|
let(:pattern) { 'spec/foobar/**/*_spec.rb' }
|
29
30
|
|
30
|
-
it { is_expected.to eq ['dummy_spec'] }
|
31
|
+
it { is_expected.to eq ['foobar/dummy_spec'] }
|
31
32
|
end
|
32
33
|
|
33
|
-
context '
|
34
|
+
context 'default path set' do
|
34
35
|
before do
|
35
|
-
create_dummy_spec_files 'spec/foobar/dummy_spec.rb', 'spec/
|
36
|
-
end
|
37
|
-
|
38
|
-
let(:pattern) { ['spec/foobar/**/*y_spec.rb', 'spec/noway/**/*_spec.rb'] }
|
39
|
-
|
40
|
-
it { is_expected.to eq ['foobar/dummy_spec', 'noway/other_spec'] }
|
41
|
-
end
|
42
|
-
|
43
|
-
context '2 paths, different root' do
|
44
|
-
before do
|
45
|
-
create_dummy_spec_files 'spec/foobar/dummy_spec.rb', 'other_path/other_spec.rb'
|
46
|
-
end
|
47
|
-
|
48
|
-
let(:pattern) { ['spec/foobar/**/*_spec.rb', 'other_path/**/*.rb'] }
|
49
|
-
|
50
|
-
it { is_expected.to eq ['dummy_spec', 'other_spec'] }
|
51
|
-
end
|
52
|
-
|
53
|
-
context 'specs in different paths, same name in middle dirs' do
|
54
|
-
before do
|
55
|
-
create_dummy_spec_files 'foobar/spec/something/dummy_spec.rb', 'spec/foobar/other_spec.rb'
|
36
|
+
create_dummy_spec_files 'spec/foobar/dummy_spec.rb', 'spec/foobar/ignored_spec.opal'
|
56
37
|
end
|
57
38
|
|
58
|
-
let(:pattern) {
|
39
|
+
let(:pattern) { 'spec/foobar/**/*_spec.rb' }
|
40
|
+
let(:default_path) { 'spec/foobar' }
|
59
41
|
|
60
|
-
it { is_expected.to eq ['
|
42
|
+
it { is_expected.to eq ['dummy_spec'] }
|
61
43
|
end
|
62
44
|
|
63
|
-
context '
|
45
|
+
context 'multiple pattern' do
|
64
46
|
before do
|
65
|
-
create_dummy_spec_files 'spec/foobar/
|
47
|
+
create_dummy_spec_files 'spec/foobar/hello1_spec.rb', 'spec/foobar/hello2_spec.rb', 'spec/foobar/bye1_spec.rb', 'spec/foobar/bye2_spec.rb'
|
66
48
|
end
|
67
49
|
|
68
|
-
let(:
|
50
|
+
let(:pattern) { %w(**/*/*1_spec.rb **/*/bye*_spec.rb) }
|
69
51
|
|
70
|
-
it { is_expected.to eq
|
52
|
+
it { is_expected.to eq %w(foobar/bye1_spec foobar/bye2_spec foobar/hello1_spec) }
|
71
53
|
end
|
72
54
|
|
73
55
|
context 'exclude pattern' do
|
@@ -80,11 +62,11 @@ describe Opal::RSpec::CachedEnvironment do
|
|
80
62
|
context 'single' do
|
81
63
|
let(:exclude_pattern) { '**/*/*1_spec.rb' }
|
82
64
|
|
83
|
-
it { is_expected.to eq
|
65
|
+
it { is_expected.to eq %w(foobar/bye2_spec foobar/hello2_spec) }
|
84
66
|
end
|
85
67
|
|
86
68
|
context 'multiple' do
|
87
|
-
let(:exclude_pattern) {
|
69
|
+
let(:exclude_pattern) { %w(**/*/*1_spec.rb **/*/bye*_spec.rb) }
|
88
70
|
|
89
71
|
it { is_expected.to eq ['foobar/hello2_spec'] }
|
90
72
|
end
|
@@ -97,7 +79,7 @@ describe Opal::RSpec::CachedEnvironment do
|
|
97
79
|
|
98
80
|
let(:files) { FileList['spec/**/h*_spec.rb'] }
|
99
81
|
|
100
|
-
it { is_expected.to eq
|
82
|
+
it { is_expected.to eq %w(foobar/hello1_spec foobar/hello2_spec) }
|
101
83
|
end
|
102
84
|
end
|
103
85
|
end
|
@@ -164,9 +164,26 @@ describe Opal::RSpec::RakeTask do
|
|
164
164
|
end
|
165
165
|
|
166
166
|
context 'pattern' do
|
167
|
+
let(:task_definition) do
|
168
|
+
Opal::RSpec::RakeTask.new(task_name) do |_, task|
|
169
|
+
task.pattern = 'spec/other/**/*_spec.rb'
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
before do
|
174
|
+
create_dummy_spec_files 'spec/other/dummy_spec.rb'
|
175
|
+
end
|
176
|
+
|
177
|
+
it { is_expected.to have_attributes pattern: 'spec/other/**/*_spec.rb' }
|
178
|
+
it { is_expected.to append_opal_path 'spec' }
|
179
|
+
it { is_expected.to require_opal_specs eq ['other/dummy_spec'] }
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'default path' do
|
167
183
|
let(:task_definition) do
|
168
184
|
Opal::RSpec::RakeTask.new(task_name) do |server, task|
|
169
185
|
task.pattern = 'spec/other/**/*_spec.rb'
|
186
|
+
task.default_path = 'spec/other'
|
170
187
|
end
|
171
188
|
end
|
172
189
|
|
@@ -182,7 +199,7 @@ describe Opal::RSpec::RakeTask do
|
|
182
199
|
|
183
200
|
context 'files' do
|
184
201
|
let(:task_definition) do
|
185
|
-
Opal::RSpec::RakeTask.new(task_name) do |
|
202
|
+
Opal::RSpec::RakeTask.new(task_name) do |_, task|
|
186
203
|
task.files = FileList['spec/other/**/*_spec.rb']
|
187
204
|
end
|
188
205
|
end
|
@@ -192,16 +209,15 @@ describe Opal::RSpec::RakeTask do
|
|
192
209
|
end
|
193
210
|
|
194
211
|
it { is_expected.to have_attributes files: FileList['spec/other/**/*_spec.rb'] }
|
195
|
-
it { is_expected.
|
196
|
-
it { is_expected.to
|
197
|
-
it { is_expected.to require_opal_specs eq ['dummy_spec'] }
|
212
|
+
it { is_expected.to append_opal_path 'spec' }
|
213
|
+
it { is_expected.to require_opal_specs eq ['other/dummy_spec'] }
|
198
214
|
end
|
199
215
|
|
200
216
|
context 'pattern and files' do
|
201
217
|
let(:expected_to_run) { false }
|
202
218
|
|
203
219
|
let(:task_definition) do
|
204
|
-
Opal::RSpec::RakeTask.new(task_name) do |
|
220
|
+
Opal::RSpec::RakeTask.new(task_name) do |_, task|
|
205
221
|
task.files = FileList['spec/other/**/*_spec.rb', 'util/**/*.rb']
|
206
222
|
task.pattern = 'spec/opal/**/*hooks_spec.rb'
|
207
223
|
end
|
@@ -6,89 +6,50 @@ describe Opal::RSpec::SprocketsEnvironment do
|
|
6
6
|
include_context :temp_dir
|
7
7
|
let(:args) { [] }
|
8
8
|
subject(:env) { Opal::RSpec::SprocketsEnvironment.new *args }
|
9
|
-
|
9
|
+
|
10
10
|
RSpec::Matchers.define :have_pathnames do |expected|
|
11
|
-
expected = expected.map {|p| File.expand_path(p) }
|
12
|
-
|
11
|
+
expected = expected.map { |p| File.expand_path(p) }
|
12
|
+
|
13
13
|
match do |actual|
|
14
14
|
actual == expected
|
15
15
|
end
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
describe '#cached' do
|
19
19
|
subject { env.cached }
|
20
|
-
|
20
|
+
|
21
21
|
it { is_expected.to be_a ::Opal::RSpec::CachedEnvironment }
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
describe '#add_spec_paths_to_sprockets' do
|
25
|
-
let(:args) { [pattern] }
|
26
|
-
|
25
|
+
let(:args) { [pattern, nil, nil, default_path] }
|
26
|
+
let(:default_path) { nil }
|
27
|
+
|
27
28
|
subject do
|
28
29
|
# in subject to allow contexts to execute before logic
|
29
30
|
env.add_spec_paths_to_sprockets
|
30
31
|
env.paths.sort
|
31
32
|
end
|
32
|
-
|
33
|
-
context '
|
33
|
+
|
34
|
+
context 'default path not set' do
|
34
35
|
before do
|
35
36
|
create_dummy_spec_files 'spec/foobar/dummy_spec.rb', 'spec/foobar/ignored_spec.opal'
|
36
37
|
end
|
37
|
-
|
38
|
+
|
38
39
|
let(:pattern) { 'spec/foobar/**/*_spec.rb' }
|
39
|
-
|
40
|
-
it { is_expected.to have_pathnames ['spec/foobar/'] }
|
41
|
-
end
|
42
|
-
|
43
|
-
context 'multiple patterns' do
|
44
|
-
before do
|
45
|
-
create_dummy_spec_files 'spec/foobar/dummy_spec.rb', 'spec/foobar/ignored_spec.opal'
|
46
|
-
end
|
47
|
-
|
48
|
-
let(:pattern) { ['spec/foobar/**/*_spec.rb', 'spec/foobar/**/*_spec.opal'] }
|
49
|
-
|
50
|
-
it { is_expected.to have_pathnames ['spec/foobar/'] }
|
51
|
-
end
|
52
|
-
|
53
|
-
context 'specs in different paths, same root' do
|
54
|
-
before do
|
55
|
-
create_dummy_spec_files 'spec/foobar/dummy_spec.rb', 'spec/noway/other_spec.rb'
|
56
|
-
end
|
57
|
-
|
58
|
-
let(:pattern) { ['spec/foobar/**/*y_spec.rb', 'spec/noway/**/*_spec.rb'] }
|
59
|
-
|
40
|
+
|
60
41
|
it { is_expected.to have_pathnames ['spec'] }
|
61
42
|
end
|
62
|
-
|
63
|
-
context '
|
64
|
-
before do
|
65
|
-
create_dummy_spec_files 'spec/foobar/dummy_spec.rb', 'other_path/other_spec.rb'
|
66
|
-
end
|
67
|
-
|
68
|
-
let(:pattern) { ['spec/foobar/**/*_spec.rb', 'other_path/**/*.rb'] }
|
69
|
-
|
70
|
-
it { is_expected.to have_pathnames ['other_path/', 'spec/foobar/'] }
|
71
|
-
end
|
72
|
-
|
73
|
-
context 'specs in different paths, same name in middle dirs' do
|
43
|
+
|
44
|
+
context 'default path set' do
|
74
45
|
before do
|
75
|
-
create_dummy_spec_files '
|
46
|
+
create_dummy_spec_files 'spec/foobar/dummy_spec.rb', 'spec/foobar/ignored_spec.opal'
|
76
47
|
end
|
77
|
-
|
78
|
-
let(:pattern) {
|
79
|
-
|
80
|
-
|
48
|
+
|
49
|
+
let(:pattern) { 'spec/foobar/**/*_spec.rb' }
|
50
|
+
let(:default_path) { 'spec/foobar' }
|
51
|
+
|
52
|
+
it { is_expected.to have_pathnames ['spec/foobar'] }
|
81
53
|
end
|
82
|
-
|
83
|
-
context 'absolute path and relative path that are not in the same tree' do
|
84
|
-
before do
|
85
|
-
create_dummy_spec_files 'spec/foobar/dummy_spec.rb', 'stuff/bar/other_spec.rb'
|
86
|
-
end
|
87
|
-
|
88
|
-
let(:files) { FileList['spec/foobar/**/*_spec.rb', 'stuff/bar/other_spec.rb'] }
|
89
|
-
let(:args) { [nil, nil, files] }
|
90
|
-
|
91
|
-
it { is_expected.to have_pathnames ['spec/foobar', 'stuff/bar'] }
|
92
|
-
end
|
93
54
|
end
|
94
55
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
rspec_filter filter '#any_instance' do
|
2
2
|
filter '#any_instance when used after the test has finished restores the original behaviour, even if the expectation fails'
|
3
3
|
filter '#any_instance when used after the test has finished restores the original behavior of a stubbed method'
|
4
|
-
filter
|
4
|
+
filter('#any_instance when directed at a method defined on a superclass mocks the method correctly').unless { at_least_opal_0_9? }
|
5
5
|
filter '#any_instance when resetting post-verification existing method with stubbing private methods restores a stubbed private method after the spec is run'
|
6
6
|
filter '#any_instance when resetting post-verification existing method with expectations private methods restores a stubbed private method after the spec is run'
|
7
7
|
filter '#any_instance passing the receiver to the implementation block when configured to pass the instance an any instance stub does not pass the instance to and_call_original'
|
8
|
-
filter
|
9
|
-
filter
|
8
|
+
filter('#any_instance setting a message expectation with an expectation is set on a method that exists after any one instance has received a message fails if the method is invoked on a second instance').unless { at_least_opal_0_9? }
|
9
|
+
filter("#any_instance setting a message expectation with an expectation is set on a method which does not exist behaves as 'exactly one instance' fails if the method is invoked on a second instance").unless { at_least_opal_0_9? }
|
10
10
|
filter('#any_instance setting a message expectation works with a SimpleDelegator subclass').unless { at_least_opal_0_9? }
|
11
11
|
filter "#any_instance when stubbing behaves as 'every instance' handles freeze and duplication correctly"
|
12
12
|
filter "#any_instance when stubbing behaves as 'every instance' handles method restoration on subclasses"
|
@@ -102,14 +102,29 @@ module Opal
|
|
102
102
|
end
|
103
103
|
|
104
104
|
def append_additional_load_paths(server)
|
105
|
-
baseline = ['spec/rspec/shared']
|
105
|
+
baseline = [base_dir, 'spec/rspec/shared']
|
106
|
+
baseline += tmp_load_paths
|
106
107
|
(baseline + additional_load_paths).each { |p| server.append_path p }
|
107
108
|
end
|
108
109
|
|
110
|
+
def get_tmp_load_path_dir
|
111
|
+
dir = Dir.mktmpdir
|
112
|
+
at_exit do
|
113
|
+
FileUtils.remove_entry dir
|
114
|
+
end
|
115
|
+
# something was clearing this if it was added via Opal.append_path, so save it
|
116
|
+
tmp_load_paths << dir
|
117
|
+
dir
|
118
|
+
end
|
119
|
+
|
120
|
+
def tmp_load_paths
|
121
|
+
@tmp_load_paths ||= []
|
122
|
+
end
|
123
|
+
|
109
124
|
def replace_with_regex(regex, description, starting_file_set, files_to_replace)
|
110
125
|
fix_these_files = starting_file_set.select { |f| files_to_replace.any? { |r| r.match(f) } }
|
111
126
|
return starting_file_set unless fix_these_files.any?
|
112
|
-
dir =
|
127
|
+
dir = get_tmp_load_path_dir
|
113
128
|
missing = []
|
114
129
|
expressions = [*regex]
|
115
130
|
fixed_temp_files = fix_these_files.map do |path|
|
@@ -131,9 +146,6 @@ module Opal
|
|
131
146
|
missing << path unless matching
|
132
147
|
temp_filename
|
133
148
|
end
|
134
|
-
at_exit do
|
135
|
-
FileUtils.remove_entry dir
|
136
|
-
end
|
137
149
|
raise "Expected to #{description} in #{fix_these_files} but we didn't find any expressions in #{missing}. Check if RSpec has been upgraded" if missing.any?
|
138
150
|
files_we_left_alone = starting_file_set - fix_these_files
|
139
151
|
files_we_left_alone + fixed_temp_files
|
@@ -166,7 +178,7 @@ module Opal
|
|
166
178
|
bad_regex = /^(.*)\\$/
|
167
179
|
fix_these_files = files.select { |f| files_with_line_continue.any? { |regex| regex.match(f) } }
|
168
180
|
return files unless fix_these_files.any?
|
169
|
-
dir =
|
181
|
+
dir = get_tmp_load_path_dir
|
170
182
|
missing = []
|
171
183
|
fixed_temp_files = fix_these_files.map do |path|
|
172
184
|
temp_filename = File.join dir, File.basename(path)
|
@@ -192,9 +204,6 @@ module Opal
|
|
192
204
|
missing << path unless found_blackslash
|
193
205
|
temp_filename
|
194
206
|
end
|
195
|
-
at_exit do
|
196
|
-
FileUtils.remove_entry dir
|
197
|
-
end
|
198
207
|
raise "Expected to fix blackslash continuation in #{fix_these_files} but we didn't find any backslashes in #{missing}. Check if RSpec has been upgraded (maybe those blackslashes are gone??)" if missing.any?
|
199
208
|
files_we_left_alone = files - fix_these_files
|
200
209
|
files_we_left_alone + fixed_temp_files
|
@@ -236,6 +245,7 @@ module Opal
|
|
236
245
|
task.timeout = 80000
|
237
246
|
stub_requires
|
238
247
|
task.files = sub_in_files
|
248
|
+
task.default_path = default_path
|
239
249
|
append_additional_load_paths server
|
240
250
|
server.debug = ENV['OPAL_DEBUG']
|
241
251
|
end
|
@@ -320,8 +330,8 @@ module Opal
|
|
320
330
|
rack.run Opal::Server.new(sprockets: sprockets_env) { |s|
|
321
331
|
s.main = 'opal/rspec/sprockets_runner'
|
322
332
|
stub_requires
|
323
|
-
sprockets_env.add_spec_paths_to_sprockets
|
324
333
|
append_additional_load_paths s
|
334
|
+
sprockets_env.add_spec_paths_to_sprockets
|
325
335
|
s.debug = ENV['OPAL_DEBUG']
|
326
336
|
}
|
327
337
|
end
|
data/vendor/spec_runner.js
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
/*
|
2
2
|
* Test runner for phantomjs
|
3
3
|
*/
|
4
|
-
var
|
4
|
+
var system = require('system');
|
5
|
+
var args = system.args.slice(1);
|
5
6
|
var page = require('webpage').create();
|
6
7
|
|
7
8
|
page.onConsoleMessage = function(msg) {
|
@@ -14,7 +15,6 @@ page.onInitialized = function() {
|
|
14
15
|
});
|
15
16
|
};
|
16
17
|
|
17
|
-
var system = require('system');
|
18
18
|
page.onCallback = function(data) {
|
19
19
|
switch (data[0]) {
|
20
20
|
case 'stdout':
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opal-rspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.0
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Beynon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opal
|
@@ -761,12 +761,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
761
761
|
version: '0'
|
762
762
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
763
763
|
requirements:
|
764
|
-
- - "
|
764
|
+
- - ">="
|
765
765
|
- !ruby/object:Gem::Version
|
766
|
-
version:
|
766
|
+
version: '0'
|
767
767
|
requirements: []
|
768
768
|
rubyforge_project:
|
769
|
-
rubygems_version: 2.4.
|
769
|
+
rubygems_version: 2.4.8
|
770
770
|
signing_key:
|
771
771
|
specification_version: 4
|
772
772
|
summary: RSpec for Opal
|