opal-rspec 1.0.0.alpha1 → 1.1.0.alpha1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 356a86acfd8bab081cb0c2a62c9e8665f8a04c1b24111bba52ea17535f4f3fd0
4
- data.tar.gz: 5496b6a23ec9f1e54a2fe5992bbe2c6daa4a8afa18b8fb4dc60b8f49c1002e91
3
+ metadata.gz: 95ab0afeb6b8db964c718d2716cd7b4f91cf022dc38eb33b7271219a7f277e8d
4
+ data.tar.gz: c256b94c07aae78d0fab3407aad4cadc5f23f2d60662551056e1c65ad70d4a20
5
5
  SHA512:
6
- metadata.gz: d39290288266cd7b2f7dce68c52551146c8f5f2e2e27b83e7a37d74ff632edec3a41d025e536aa9d8d51cf77753d0f165207eef65ccb4717c4d8efb2898acf0a
7
- data.tar.gz: d83cff5a126074aeebfdfe642895beb0964e815a963c1f6a61b489e8a96fcd5d91f426c8124ab2652b03d613c4e2adda53ab7c2a501d0f10511d698f7cf59a17
6
+ metadata.gz: 0567fb3f9293fbcafa0c6990dcdfb36bf2e40936ff75b6f8879a798628c3a9384842e95da555851a9ff9abb876409d148434e066e8071bd62a525670760ed914
7
+ data.tar.gz: 0ee3ad1ace7524f5daf1025a7671309d23905bd9f6f87c280a297b8817c4292943a6a773cbe7c4dd208cf70836b5f197e2cec95924912880b13119136bc64419
@@ -16,7 +16,7 @@ jobs:
16
16
  matrix:
17
17
  os: [ 'ubuntu-latest' ]
18
18
  ruby: [ ruby-head, 3.1, "3.0", 2.7 ]
19
- opal: [ master, 1.6.alpha1 ]
19
+ opal: [ master, 1.6, 1.7 ]
20
20
 
21
21
  runs-on: ${{ matrix.os }}
22
22
 
data/CHANGELOG.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # Opal-RSpec Changelog
2
2
 
3
- ## 1.0.0.alpha1 - 2022-11-11
3
+ ## 1.1.0.alpha1 - 2023-09-16
4
+
5
+ - Support Opal headless browser runners
6
+
7
+ - (Almost) full support for `rspec` CLI util (which we run using `opal-rspec` command)
8
+ * Focus works, you can type `opal-rspec spec-opal-passing/tautology_spec.rb:8` to select a given test
9
+ * Most of other command line switches of `opal-rspec` work
10
+ * You can use a `.rspec-opal` configuration file akin to `.rspec` with the regular RSpec
11
+ * Just like with RSpec, switches set in this file propagate everywhere (eg. Rake task), so this is from now a prefered place to set up paths, etc.
12
+
13
+ - Compatibility for upcoming Opal v1.8
14
+
15
+
16
+ ## 1.0.0 - 2022-11-24
4
17
 
5
18
  - Drop support for anything below Opal v1.6.alpha1
6
19
 
@@ -14,6 +27,9 @@
14
27
  * In `around` blocks, you must call `example.run_await` instead of just `example.run`
15
28
  * Only `PromiseV2` is supported (`PromiseV1` may work, but you should migrate your application to use `PromiseV2` nevertheless, in Opal 2.0 it will become the default)
16
29
 
30
+ - Drop a requirement of `opal-sprockets`
31
+ * Sprockets support is still provided, but you need to manually `require "opal/rspec/sprockets"`
32
+
17
33
 
18
34
  ## 0.8.0 - 2021-12-01
19
35
 
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
- unless Dir['rspec{,-{core,expectations,mocks,support}}/upstream'].any?
4
+ unless Dir[__dir__ + '/rspec{,-{core,expectations,mocks,support}}/upstream'].any?
5
5
  raise 'Run: "git submodule update --init" to get RSpec sources'
6
6
  end
7
7
 
@@ -26,3 +26,4 @@ end
26
26
  gem 'opal-sprockets', '>=1.0'
27
27
 
28
28
  gem 'puma'
29
+ gem 'rack', '<3'
data/README.md CHANGED
@@ -135,6 +135,10 @@ end
135
135
 
136
136
  If you don't specify a runner using `task.runner`, a default one is Node. In this case you can also use `RUNNER=chrome` to run a particular test with Headless Chromium.
137
137
 
138
+ *(since v1.1.0)*
139
+
140
+ You can also put default options into an `.rspec-opal` file, akin to how RSpec works. Those options will be propagated to Rake task, invocations of `Opal::RSpec::Runner` and also the `opal-rspec` command line tool.
141
+
138
142
  ### Run specs in Node.js
139
143
 
140
144
  Same options as above, you can use the `RUNNER=node` environment variable
@@ -157,6 +161,11 @@ Opal::RSpec::RakeTask.new(:default) do |server, task|
157
161
  end
158
162
  ```
159
163
 
164
+ *(since v1.1.0)*
165
+
166
+ While `server` value runs a server, you can here supply also respectively
167
+ `chrome`, `firefox` or `safari` to run the test using a headless browser.
168
+
160
169
  ### Run specs in a browser (Sprockets, deprecated)
161
170
 
162
171
  `opal-rspec` can use sprockets to build and serve specs over a simple rack
@@ -347,7 +356,7 @@ When updating the RSpec versions, after updating the submodule revisions, you ma
347
356
 
348
357
  (The MIT License)
349
358
 
350
- Copyright (C) 2022 by hmdne and the Opal contributors
359
+ Copyright (C) 2022-2023 by hmdne and the Opal contributors
351
360
  Copyright (C) 2015 by Brady Wied
352
361
  Copyright (C) 2013 by Adam Beynon
353
362
 
data/exe/opal-rspec CHANGED
@@ -4,81 +4,23 @@
4
4
 
5
5
  require 'rake'
6
6
  require 'opal/rspec/runner'
7
+ require 'opal/rspec/configuration_parser'
7
8
 
8
9
  require 'shellwords'
9
- require 'optparse'
10
10
 
11
- options = {}
12
- OptionParser.new do |parser|
13
- parser.on('--init', 'Initialize your project with Opal-RSpec.') do |_cmd|
14
- Opal::RSpec::ProjectInitializer.new.run
15
- exit
16
- end
17
-
18
- parser.on('-c', '--[no-]color', '--[no-]colour', 'Enable color in the output.') do |o|
19
- options[:color] = o
20
- end
21
-
22
- parser.on('-f', '--format FORMATTER', 'Choose a formatter.',
23
- ' [p]rogress (default - dots)',
24
- ' [d]ocumentation (group and example names)',
25
- ' [h]tml',
26
- ' [j]son',
27
- ' custom formatter class name') do |o|
28
- options[:formatters] ||= []
29
- options[:formatters] << [o]
30
- end
31
-
32
- parser.on('-r', '--require PATH', 'Require a file.') do |path|
33
- options[:requires] ||= []
34
- options[:requires] << path
35
- end
36
-
37
- parser.on('-I PATH', 'Specify PATH to add to $LOAD_PATH (may be used more than once).') do |dir|
38
- options[:includes] ||= []
39
- options[:includes] << dir
40
- end
41
-
42
- parser.on('-P', '--pattern PATTERN', 'Load files matching pattern (default: "spec-opal/**/*_spec.rb").') do |o|
43
- options[:pattern] = o
44
- end
45
-
46
- parser.on('--default-path PATH', 'Set the default path where RSpec looks for examples (can',
47
- ' be a path to a file or a directory).') do |path|
48
- options[:default_path] = path
49
- end
50
-
51
- parser.separator 'Opal specific options:'
52
-
53
- parser.on('-R', '--runner NAME', 'Use a different JS runner (default is nodejs)') do |name|
54
- options[:runner] = name
55
- end
56
- end.parse!
57
-
58
- runner = Opal::RSpec::Runner.new do |server, config|
59
- spec_opts = []
60
- spec_opts += ['--color', options[:color]] if options[:color]
61
- options[:requires].each {|req| spec_opts += ['-r', req]} if options[:requires]
62
- raise "can accept only one formatter at this time" if options[:formatters] && options[:formatters].size != 1
63
- spec_opts += ['--format', options[:formatters].flatten.first] if options[:formatters]
64
- raw_files = ARGV
65
- raw_files = ['spec-opal'] if raw_files.empty?
11
+ parser = Opal::RSpec::Core::Parser.new(ARGV)
12
+ options = parser.parse
66
13
 
67
- files = raw_files.flat_map do |file|
68
- if File.directory? file
69
- Dir[File.join(file, '**{,/*/**}/*_spec{.js,}.rb')]
70
- elsif File.file? file
71
- file
72
- else
73
- raise "Can't stat path: #{file}"
74
- end
14
+ case options[:runner]
15
+ when String, nil
16
+ runner = Opal::RSpec::Runner.new do |server, config|
17
+ config.runner = options.delete(:runner)
18
+ config.spec_opts = options
75
19
  end
76
20
 
77
- options[:includes].each {|dir| server.append_path dir} if options[:includes]
78
- config.default_path = options[:default_path] if options[:default_path]
79
- config.spec_opts = spec_opts.shelljoin
80
- config.files = files
21
+ runner.run
22
+ else
23
+ exit options[:runner].(ARGV, $stderr, $stdout)
81
24
  end
82
25
 
83
- runner.run
84
26
 
@@ -0,0 +1,46 @@
1
+ require 'optparse'
2
+
3
+ module Opal; module RSpec; module Core; end; end; end
4
+ # Load necessary files under Opal's namespace, so as not to conflict with RSpec if it's being loaded too.
5
+ # Later, we will monkey-patch those methods.
6
+ load __dir__ + "/../../../rspec-core/upstream/lib/rspec/core/invocations.rb", ::Opal
7
+ load __dir__ + "/../../../rspec-core/upstream/lib/rspec/core/option_parser.rb", ::Opal
8
+
9
+ class Opal::RSpec::Core::Parser
10
+ alias parser_before_opal parser
11
+
12
+ def parser(options)
13
+ parser_before_opal(options).tap do |parser|
14
+ parser.banner = "Usage: opal-rspec [options] [files or directories]\n\n"
15
+
16
+ parser.separator ''
17
+ parser.separator ' **** Opal specific options ****'
18
+ parser.separator ''
19
+
20
+ parser.on('-R', '--runner NAME', 'Use a different JS runner (default is nodejs)') do |name|
21
+ options[:runner] = name
22
+ end
23
+
24
+ parser.separator ''
25
+ parser.separator ' **** Help ****'
26
+ parser.separator ''
27
+ end
28
+ end
29
+ end
30
+
31
+ class Opal::RSpec::Core::Invocations::PrintVersion
32
+ alias call_before_opal call
33
+
34
+ def call(options, err, out)
35
+ exitcode = call_before_opal(options, err, out)
36
+ out.puts "Opal #{Opal::VERSION}"
37
+ out.puts " - opal-rspec #{Opal::RSpec::VERSION}"
38
+ exitcode
39
+ end
40
+ end
41
+
42
+ module Opal::RSpec::Support
43
+ def self.require_rspec_core(arg)
44
+ require "opal/rspec/"+arg
45
+ end
46
+ end
@@ -2,24 +2,31 @@ require 'pathname'
2
2
  require 'rake'
3
3
  # require the bundled RSpec's file and don't rely on the load path in case opal-rspec is included in a project's
4
4
  # Gemfile without rspec also being in the Gemfile
5
- require_relative '../../../rspec-core/upstream/lib/rspec/core/ruby_project'
5
+ load __dir__+'/../../../rspec-core/upstream/lib/rspec/core/ruby_project.rb', ::Opal
6
6
 
7
7
  module Opal
8
8
  module RSpec
9
9
  class Locator
10
- include ::RSpec::Core::RubyProject
10
+ include ::Opal::RSpec::Core::RubyProject
11
11
 
12
12
  DEFAULT_GLOB = '**{,/*/**}/*_spec{.js,}.{rb,opal}'
13
- DEFAULT_PATTERN = "spec-opal/#{DEFAULT_GLOB}"
14
13
  DEFAULT_DEFAULT_PATH = 'spec-opal'
15
14
 
16
15
  attr_accessor :spec_pattern, :spec_exclude_pattern, :spec_files, :default_path
17
16
 
18
17
  def initialize(pattern: nil, exclude_pattern: nil, files: nil, default_path: nil)
19
- @spec_pattern = pattern || DEFAULT_PATTERN
20
- @spec_exclude_pattern = exclude_pattern
18
+ @spec_exclude_pattern = Array(exclude_pattern)
21
19
  @spec_files = files
22
20
  @default_path = default_path || DEFAULT_DEFAULT_PATH
21
+ @spec_pattern = Array(pattern || DEFAULT_GLOB)
22
+
23
+ @spec_pattern = @spec_pattern.map do |pattern|
24
+ pattern.sub(/\A#{Regexp.escape(@default_path)}/, '')
25
+ end
26
+
27
+ @spec_exclude_pattern = @spec_exclude_pattern.map do |pattern|
28
+ pattern.sub(/\A#{Regexp.escape(@default_path)}/, '')
29
+ end
23
30
  end
24
31
 
25
32
  def determine_root
@@ -27,11 +34,28 @@ module Opal
27
34
  end
28
35
 
29
36
  def get_spec_load_paths
30
- [@default_path].map { |dir| File.join(root, dir) }
37
+ [File.join(root, @default_path)]
38
+ end
39
+
40
+ def get_matching_files_under(path: )
41
+ FileList[*@spec_pattern.map { |i| "#{path}/#{i}" }]
42
+ .exclude(*@spec_exclude_pattern.map { |i| "#{path}/#{i}" })
31
43
  end
32
44
 
33
45
  def get_opal_spec_requires
34
- files = @spec_files || FileList[*@spec_pattern].exclude(*@spec_exclude_pattern)
46
+ if !@spec_files || @spec_files.empty?
47
+ files = get_matching_files_under(path: @default_path)
48
+ else
49
+ files = @spec_files.map do |file|
50
+ file = file.split(/[\[:]/).first
51
+ if File.directory?(file)
52
+ get_matching_files_under(path: file).to_a
53
+ else
54
+ file
55
+ end
56
+ end.flatten
57
+ files = FileList[*files]
58
+ end
35
59
  files.uniq.map { |file| File.expand_path file }
36
60
  end
37
61
  end
@@ -1,2 +1 @@
1
- --color
2
1
  --require spec_helper
@@ -1,4 +1,88 @@
1
+ # This file was generated by the `opal-rspec --init` command. Conventionally, all
2
+ # specs live under a `spec-opal` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec-opal` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
1
16
  RSpec.configure do |config|
17
+ # rspec-expectations config goes here. You can use an alternate
18
+ # assertion/expectation library such as wrong or the stdlib/minitest
19
+ # assertions if you prefer.
20
+ config.expect_with :rspec do |expectations|
21
+ # This option will default to `true` in RSpec 4. It makes the `description`
22
+ # and `failure_message` of custom matchers include text for helper methods
23
+ # defined using `chain`, e.g.:
24
+ # be_bigger_than(2).and_smaller_than(4).description
25
+ # # => "be bigger than 2 and smaller than 4"
26
+ # ...rather than:
27
+ # # => "be bigger than 2"
28
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
29
+ end
30
+
31
+ # rspec-mocks config goes here. You can use an alternate test double
32
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
33
+ config.mock_with :rspec do |mocks|
34
+ # Prevents you from mocking or stubbing a method that does not exist on
35
+ # a real object. This is generally recommended, and will default to
36
+ # `true` in RSpec 4.
37
+ mocks.verify_partial_doubles = true
38
+ end
39
+
40
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
41
+ # have no way to turn it off -- the option exists only for backwards
42
+ # compatibility in RSpec 3). It causes shared context metadata to be
43
+ # inherited by the metadata hash of host groups and examples, rather than
44
+ # triggering implicit auto-inclusion in groups with matching metadata.
45
+ config.shared_context_metadata_behavior = :apply_to_host_groups
46
+
47
+ # The settings below are suggested to provide a good initial experience
48
+ # with RSpec, but feel free to customize to your heart's content.
49
+ =begin
50
+ # This allows you to limit a spec run to individual examples or groups
51
+ # you care about by tagging them with `:focus` metadata. When nothing
52
+ # is tagged with `:focus`, all examples get run. RSpec also provides
53
+ # aliases for `it`, `describe`, and `context` that include `:focus`
54
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
55
+ config.filter_run_when_matching :focus
56
+
57
+ # Allows RSpec to persist some state between runs in order to support
58
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
59
+ # you configure your source control system to ignore this file.
60
+ config.example_status_persistence_file_path = "spec/examples.txt"
61
+
62
+ # Limits the available syntax to the non-monkey patched syntax that is
63
+ # recommended. For more details, see:
64
+ # https://relishapp.com/rspec/rspec-core/docs/configuration/zero-monkey-patching-mode
65
+ config.disable_monkey_patching!
66
+
67
+ # This setting enables warnings. It's recommended, but in some cases may
68
+ # be too noisy due to issues in dependencies.
69
+ config.warnings = true
70
+
71
+ # Many RSpec users commonly either run the entire suite or an individual
72
+ # file, and it's useful to allow more verbose output when running an
73
+ # individual spec file.
74
+ if config.files_to_run.one?
75
+ # Use the documentation formatter for detailed output,
76
+ # unless a formatter has already been configured
77
+ # (e.g. via a command-line flag).
78
+ config.default_formatter = "doc"
79
+ end
80
+
81
+ # Print the 10 slowest examples and example groups at the
82
+ # end of the spec run, to help surface which specs are running
83
+ # particularly slow.
84
+ config.profile_examples = 10
85
+
2
86
  # Run specs in random order to surface order dependencies. If you find an
3
87
  # order dependency and want to debug it, you can fix the order by providing
4
88
  # the seed, which is printed after each run.
@@ -10,4 +94,5 @@ RSpec.configure do |config|
10
94
  # test failures related to randomization by passing the same `--seed` value
11
95
  # as the one that triggered the failure.
12
96
  Kernel.srand config.seed
97
+ =end
13
98
  end
@@ -19,6 +19,17 @@ module Opal
19
19
  end
20
20
 
21
21
  def run
22
+ puts <<~EOF
23
+ ** Do note, that Opal-RSpec defaults to the following paths:
24
+ ** - config file: .rspec-opal
25
+ ** - spec directory: spec-opal
26
+ ** - program: lib-opal
27
+ **
28
+ ** If you want to share Opal specs with Ruby specs, you will
29
+ ** need to put the following into your .rspec-opal:
30
+ **
31
+ ** -Ilib --default-path=spec
32
+ EOF
22
33
  copy_template DOT_RSPEC_FILE
23
34
  copy_template SPEC_HELPER_FILE
24
35
  end
@@ -9,7 +9,24 @@ require 'opal/rspec/locator'
9
9
  module Opal
10
10
  module RSpec
11
11
  class Runner
12
- attr_accessor :pattern, :requires, :exclude_pattern, :files, :default_path, :runner, :arity_checking, :spec_opts, :cli_options
12
+ attr_accessor :runner, :arity_checking, :spec_opts, :cli_options, :files
13
+
14
+ # Delegate property changes to spec_opts
15
+ def self.spec_opts_accessor(*names)
16
+ names.each do |name|
17
+ define_method name do
18
+ spec_opts[name]
19
+ end
20
+ define_method :"#{name}=" do |value|
21
+ spec_opts[name] = value
22
+ end
23
+ end
24
+ end
25
+
26
+ spec_opts_accessor :libs, :requires, :pattern, :exclude_pattern, :default_path, :files_or_directories_to_run
27
+
28
+ alias files files_or_directories_to_run
29
+ alias files= files_or_directories_to_run=
13
30
 
14
31
  def timeout= _
15
32
  warn "deprecated: setting timeout has no effect"
@@ -33,11 +50,11 @@ module Opal
33
50
  end
34
51
 
35
52
  def requires
36
- @requires ||= []
53
+ spec_opts[:requires] ||= []
37
54
  end
38
55
 
39
56
  def spec_opts
40
- @spec_opts ||= ENV['SPEC_OPTS']
57
+ @spec_opts = Opal::RSpec.convert_spec_opts(@spec_opts)
41
58
  end
42
59
 
43
60
  def get_load_asset_code(server)
@@ -80,9 +97,16 @@ module Opal
80
97
  @legacy_server_proxy = LegacyServerProxy.new
81
98
  block.call(@legacy_server_proxy, self) if block_given? # for compatibility
82
99
 
83
- raise 'Cannot supply both a pattern and files!' if self.files and self.pattern
100
+ raise 'Cannot supply both a pattern and files!' if self.files \
101
+ && !self.files.empty? \
102
+ && self.pattern
84
103
 
85
- locator = ::Opal::RSpec::Locator.new pattern: self.pattern, exclude_pattern: self.exclude_pattern, files: self.files, default_path: self.default_path
104
+ append_opts_from_config_file
105
+
106
+ locator = ::Opal::RSpec::Locator.new pattern: self.pattern,
107
+ exclude_pattern: self.exclude_pattern,
108
+ files: self.files,
109
+ default_path: self.default_path
86
110
 
87
111
  options = []
88
112
  options << '--arity-check' if arity_checking?
@@ -93,21 +117,55 @@ module Opal
93
117
  options << '--missing-require=ignore'
94
118
  options += @legacy_server_proxy.to_cli_options
95
119
 
96
- Opal.paths.each { |p| options << "-I#{p}" }
97
- locator.get_spec_load_paths.each { |p| options << "-I#{p}" }
120
+ load_paths = [Opal.paths, locator.get_spec_load_paths, self.libs].compact.sum([]).uniq
121
+
122
+ load_paths.each { |p| options << "-I#{p}" }
98
123
  requires.each { |p| options << "-r#{p}" }
99
- locator.get_opal_spec_requires.each { |p| options << "-r#{p}" }
124
+ locator.get_opal_spec_requires.each { |p| options << "-p#{p}" }
100
125
  ::Opal::Config.stubbed_files.each { |p| options << "-s#{p}" }
101
126
 
102
127
  options += @cli_options if @cli_options
103
- bootstrap_code = [
104
- ::Opal::RSpec.spec_opts_code(spec_opts),
105
- '::RSpec::Core::Runner.autorun',
106
- ].join(';')
128
+
129
+ bootstrap_code = ::Opal::RSpec.spec_opts_code(spec_opts)
107
130
 
108
131
  @args = "#{options.map(&:shellescape).join ' '} -e #{bootstrap_code.shellescape}"
109
132
  end
110
133
 
134
+ def append_opts_from_config_file
135
+ self.libs ||= []
136
+ self.requires ||= []
137
+
138
+ config_location = nil
139
+ path = self.default_path || "spec-opal"
140
+
141
+ # Locate config file
142
+ begin
143
+ loop do
144
+ if File.exist?(File.join(path, ".rspec-opal"))
145
+ path = File.join(path, ".rspec-opal")
146
+ break
147
+ else
148
+ new_path = File.expand_path("..", path)
149
+ return if new_path == path
150
+ path = new_path
151
+ end
152
+ end
153
+ rescue e
154
+ # we've gone too far beyond our permissions without finding a config file
155
+ return
156
+ end
157
+
158
+ if path
159
+ config_opts = Opal::RSpec.convert_spec_opts(File.read(path))
160
+ else
161
+ return
162
+ end
163
+
164
+ self.spec_opts = config_opts.merge(spec_opts) do |key, oldval, newval|
165
+ [:libs, :requires].include?(key) ? oldval + newval : newval
166
+ end
167
+ end
168
+
111
169
  def options
112
170
  {
113
171
  pattern: pattern,
@@ -1,5 +1,10 @@
1
1
  module Opal
2
2
  module RSpec
3
- VERSION = '1.0.0.alpha1'
3
+ VERSION = '1.1.0.alpha1'
4
4
  end
5
5
  end
6
+
7
+ load __dir__ + "/../../../rspec-core/upstream/lib/rspec/core/version.rb", ::Opal
8
+ load __dir__ + "/../../../rspec-expectations/upstream/lib/rspec/expectations/version.rb", ::Opal
9
+ load __dir__ + "/../../../rspec-mocks/upstream/lib/rspec/mocks/version.rb", ::Opal
10
+ load __dir__ + "/../../../rspec-support/upstream/lib/rspec/support/version.rb", ::Opal
data/lib/opal/rspec.rb CHANGED
@@ -2,6 +2,7 @@ require 'opal'
2
2
  require 'opal-sprockets'
3
3
  require 'opal/rspec/version'
4
4
  require 'opal/rspec/runner'
5
+ require 'opal/rspec/configuration_parser'
5
6
 
6
7
  # Just register our opal code path with opal build tools
7
8
  Opal.append_path File.expand_path('../../../lib-opal', __FILE__)
@@ -19,32 +20,31 @@ module Opal
19
20
  module RSpec
20
21
  autoload :ProjectInitializer, 'opal/rspec/project_initializer'
21
22
 
23
+ def self.convert_spec_opts(opts)
24
+ opts ||= ENV['SPEC_OPTS'] || {}
25
+
26
+ unless opts.is_a? Hash
27
+ opts = Shellwords.split(opts) if opts.is_a? String
28
+ opts = Opal::RSpec::Core::Parser.parse(opts || [])
29
+ end
30
+
31
+ opts
32
+ end
33
+
22
34
  def self.spec_opts_code(spec_opts)
35
+ spec_opts = convert_spec_opts(spec_opts)
36
+
23
37
  code = []
24
- if spec_opts && !spec_opts.empty?
25
- code << 'RSpec.configure do |config|'
26
-
27
- if (match = /--(no-)?color\b/.match(spec_opts))
28
- color_value = !match.captures[0]
29
- # Have to use instance_variable_set because config.color= is designed to not allow overriding color once it's set, but
30
- # we do not yet have true SPEC_OPTS parsing via RSpec config to get it initially set
31
- code << "config.instance_variable_set(:@color, #{color_value})"
32
- end
33
-
34
- if (requires = spec_opts.scan(/(?:--require|-r) \S+/)).any?
35
- requires.map {|r| /--require (.*)/.match(r).captures[0]}.each do |req|
36
- code << %{require "#{req}"}
37
- end
38
- end
39
-
40
- if (match = /--format (\S+)/.match(spec_opts))
41
- formatter = match.captures[0]
42
- code << %{config.formatter = "#{formatter}"}
43
- end
44
-
45
- code << 'end'
46
- end
47
- code.join('; ')
38
+ code << '# await: true'
39
+
40
+ # New API - passthru options
41
+ spec_opts[:files_or_directories_to_run] ||= []
42
+
43
+ code << "$rspec_opts = #{spec_opts.inspect}"
44
+ code << "$0 = 'opal-rspec'"
45
+
46
+ code << '::RSpec::Core::Runner.invoke.__await__'
47
+ code.join("\n")
48
48
  end
49
49
  end
50
50
  end
@@ -1,5 +1,9 @@
1
1
  require 'opal/rspec/formatter/browser_formatter'
2
2
 
3
3
  RSpec.configure do |config|
4
- config.default_formatter = ::Opal::RSpec::BrowserFormatter
4
+ if OPAL_PLATFORM.nil?
5
+ # We want the browser formatter ONLY for the real browser, not
6
+ # our headless browser runners.
7
+ config.default_formatter = ::Opal::RSpec::BrowserFormatter
8
+ end
5
9
  end
@@ -1,20 +1,112 @@
1
- class ::RSpec::Core::Configuration
2
- # This needs to be implemented if/when we allow the Opal side to decide what files to run
1
+ # backtick_javascript: true
2
+
3
+ require 'rspec/core/configuration'
4
+
5
+ module ::RSpec; module Core; class Configuration
3
6
  def files_or_directories_to_run=(*files)
4
- @files_or_directories_to_run = []
7
+ files = files.flatten
8
+
9
+ # patch: rspec -> opal-rspec
10
+ if (command == 'opal-rspec' || Runner.running_in_drb?) && default_path && files.empty?
11
+ files << default_path
12
+ end
13
+
14
+ @files_or_directories_to_run = files
5
15
  @files_to_run = nil
6
16
  end
7
17
 
8
18
  def requires=(paths)
9
19
  # can't change requires @ this stage, this method calls RubyProject which will crash on Opal
10
20
  end
11
- end
21
+
22
+ def remove_ruby_ext(str)
23
+ str.gsub(/(?:\.js)?\.(?:rb|opal|\{rb,opal\})\z/, '')
24
+ end
25
+
26
+ def glob_to_re(path, pattern)
27
+ pattern = remove_ruby_ext(pattern)
28
+ path = "" if pattern.start_with?(path)
29
+ path += "/" unless path.end_with?("/")
30
+ re = Regexp.escape(pattern).gsub('\*\*', '.*?')
31
+ .gsub('\*', '[^/]*?')
32
+ .gsub('\?', '[^/]')
33
+ .gsub(/\\{(.*?)\\}/) {
34
+ '(?:' + $1.gsub(",", "|") + ')'
35
+ }
36
+ re = "(?:^|/)" + Regexp.escape(path) + re + "$"
37
+ # Strip the first `/` so it acts more like the intention of `**`
38
+ re = re.gsub("/.*?", ".*?")
39
+ # Strip the `/./`
40
+ re = re.gsub("/\\.\\/", "\\/")
41
+ re = re.gsub("/)\\.\\/", "\\/)")
42
+ Regexp.new(re)
43
+ end
44
+
45
+ # Only load from loaded files
46
+ def get_matching_files(path, pattern)
47
+ `Object.keys(Opal.modules)`.grep(glob_to_re(path, pattern)).sort
48
+ end
49
+
50
+ # A crude logic to check if a path is a directory perhaps...
51
+ # This ought to work in places where we don't have a filesystem.
52
+ def is_directory?(path)
53
+ return true if path.end_with? '/'
54
+ # This is passed with ":" if we run something like:
55
+ # opal-rspec spec-opal-passing/tautology_spec.rb:8
56
+ return false if path =~ /\[[0-9:]+\]$|:[0-9]+$/
57
+ # Ruby files are certainly not directories
58
+ return false if ['.rb', '.opal'].any? { |i| path.end_with? i }
59
+ # Otherwise, let's check for modules
60
+ !`Object.keys(Opal.modules)`.any? { |i| i.end_with?("/"+remove_ruby_ext(path)) }
61
+ end
62
+
63
+ def get_files_to_run(paths)
64
+ files = paths_to_check(paths).flat_map do |path|
65
+ path = path.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
66
+ is_directory?(path) ? gather_directories(path) : extract_location(path)
67
+ end.uniq
68
+
69
+ return files unless only_failures?
70
+ relative_files = files.map { |f| Metadata.relative_path(File.expand_path f) }
71
+ intersection = (relative_files & spec_files_with_failures.to_a)
72
+ intersection.empty? ? files : intersection
73
+ end
74
+
75
+ def opal_special_load(file)
76
+ file = remove_ruby_ext(file)
77
+ long_file = `Object.keys(Opal.modules)`.find { |i| i.end_with?(file) }
78
+ `Opal.modules[file] = Opal.modules[long_file]` if long_file
79
+
80
+ # Let's try a normalized load
81
+ `Opal.load_normalized(file)`
82
+ rescue LoadError
83
+ # Otherwise, a regular require
84
+ require file
85
+ end
86
+
87
+ alias load_file_handling_errors_before_opal load_file_handling_errors
88
+
89
+ def load_file_handling_errors(method, file)
90
+ load_file_handling_errors_before_opal(:opal_special_load, file)
91
+ end
92
+ end; end; end
12
93
 
13
94
  class ::RSpec::Core::ConfigurationOptions
95
+ # Opal-RSpec should work without need of filesystem
96
+ # access, therefore we can't support access to the
97
+ # options file.
14
98
  def options_file_as_erb_string(path)
15
99
  # ERB.new(File.read(path), nil, '-').result(binding)
16
100
  # ERB.new(File.read(path), nil, '-')
17
101
  ''
18
102
  end
103
+
104
+ # Pass command line options directly
105
+ def command_line_options
106
+ $rspec_opts || {}
107
+ end
19
108
  end
20
109
 
110
+ # Set the default path to spec-opal, to be overwritten
111
+ # later.
112
+ RSpec.configuration.default_path = "spec-opal"
@@ -1,3 +1,5 @@
1
+ # backtick_javascript: true
2
+
1
3
  # Random causes problems that can lock up a browser (see README)
2
4
  class ::RSpec::Core::Ordering::Random
3
5
  HIDE_RANDOM_WARNINGS = false
@@ -1,3 +1,5 @@
1
+ # backtick_javascript: true
2
+
1
3
  module Opal
2
4
  module RSpec
3
5
  class DocumentIO < IO
@@ -1,3 +1,5 @@
1
+ # backtick_javascript: true
2
+
1
3
  module Opal
2
4
  module RSpec
3
5
  class Element
@@ -1,3 +1,5 @@
1
+ # backtick_javascript: true
2
+
1
3
  require_relative 'noop_flush_string_io'
2
4
  require_relative 'element'
3
5
 
@@ -1,3 +1,5 @@
1
+ # await: true
2
+
1
3
  # This file is used by Opal::Server to basically load all spec files that
2
4
  # can be found in the spec/ directory.
3
5
 
@@ -10,5 +12,5 @@ require <%= s.inspect %>
10
12
  <% end %>
11
13
  require 'opal/rspec/spec_opts'
12
14
 
13
- ::RSpec::Core::Runner.autorun
15
+ ::RSpec::Core::Runner.invoke.__await__
14
16
  ::Kernel.exit
data/opal-rspec.gemspec CHANGED
@@ -25,7 +25,6 @@ Gem::Specification.new do |spec|
25
25
  spec.require_paths = ["lib"]
26
26
 
27
27
  spec.add_dependency 'opal', ['>= 1.6.0a', '< 2.0']
28
- spec.add_dependency 'opal-sprockets', '< 2.0'
29
28
  spec.add_dependency 'rake', '>= 12.0'
30
29
 
31
30
  spec.add_development_dependency 'bundler'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opal-rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.alpha1
4
+ version: 1.1.0.alpha1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Beynon
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2022-11-11 00:00:00.000000000 Z
13
+ date: 2023-09-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: opal
@@ -32,20 +32,6 @@ dependencies:
32
32
  - - "<"
33
33
  - !ruby/object:Gem::Version
34
34
  version: '2.0'
35
- - !ruby/object:Gem::Dependency
36
- name: opal-sprockets
37
- requirement: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "<"
40
- - !ruby/object:Gem::Version
41
- version: '2.0'
42
- type: :runtime
43
- prerelease: false
44
- version_requirements: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - "<"
47
- - !ruby/object:Gem::Version
48
- version: '2.0'
49
35
  - !ruby/object:Gem::Dependency
50
36
  name: rake
51
37
  requirement: !ruby/object:Gem::Requirement
@@ -308,6 +294,7 @@ files:
308
294
  - lib/opal-rspec.rb
309
295
  - lib/opal/rspec.rb
310
296
  - lib/opal/rspec/cached_environment.rb
297
+ - lib/opal/rspec/configuration_parser.rb
311
298
  - lib/opal/rspec/locator.rb
312
299
  - lib/opal/rspec/project_initializer.rb
313
300
  - lib/opal/rspec/project_initializer/.rspec-opal
@@ -969,7 +956,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
969
956
  - !ruby/object:Gem::Version
970
957
  version: 1.3.1
971
958
  requirements: []
972
- rubygems_version: 3.3.7
959
+ rubygems_version: 3.4.10
973
960
  signing_key:
974
961
  specification_version: 4
975
962
  summary: RSpec for Opal