opal-rspec 1.0.0 → 1.1.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c68bbe588e9f4799786a07239f9b09201d9ff8852eba3d0427d475d2c7d26eb4
4
- data.tar.gz: f879318367b656f9da5068088a05093d02aa0ff30271b4886983e1f007e310ff
3
+ metadata.gz: 95ab0afeb6b8db964c718d2716cd7b4f91cf022dc38eb33b7271219a7f277e8d
4
+ data.tar.gz: c256b94c07aae78d0fab3407aad4cadc5f23f2d60662551056e1c65ad70d4a20
5
5
  SHA512:
6
- metadata.gz: 151736466857ed69765cac6061530352e9c43a37323a0c2cc5b75acffbddf2f4cccffe5dcd6d48e6ef1aa8ac88962fe2be6f38e283758adb5a76562823faec42
7
- data.tar.gz: b68d39d4ac974ccfa7893995e117329ffeaaf56e0f3999e8a39648a8d03e24e46f1bcd927df537618fba69348ccad9e576f0fae3864f95fe0a68c1e646f0133d
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,5 +1,18 @@
1
1
  # Opal-RSpec Changelog
2
2
 
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
+
3
16
  ## 1.0.0 - 2022-11-24
4
17
 
5
18
  - Drop support for anything below Opal v1.6.alpha1
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'
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
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
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-24 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
@@ -294,6 +294,7 @@ files:
294
294
  - lib/opal-rspec.rb
295
295
  - lib/opal/rspec.rb
296
296
  - lib/opal/rspec/cached_environment.rb
297
+ - lib/opal/rspec/configuration_parser.rb
297
298
  - lib/opal/rspec/locator.rb
298
299
  - lib/opal/rspec/project_initializer.rb
299
300
  - lib/opal/rspec/project_initializer/.rspec-opal
@@ -951,11 +952,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
951
952
  version: '0'
952
953
  required_rubygems_version: !ruby/object:Gem::Requirement
953
954
  requirements:
954
- - - ">="
955
+ - - ">"
955
956
  - !ruby/object:Gem::Version
956
- version: '0'
957
+ version: 1.3.1
957
958
  requirements: []
958
- rubygems_version: 3.3.7
959
+ rubygems_version: 3.4.10
959
960
  signing_key:
960
961
  specification_version: 4
961
962
  summary: RSpec for Opal