├── .codeclimate.yml ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── .rspec ├── .yardopts ├── Appraisals ├── CHANGELOG.md ├── Gemfile ├── README.md ├── Rakefile ├── bin ├── console ├── generate_requires ├── opal ├── opal-rspec └── setup ├── config.ru ├── diff-lcs └── spec │ ├── files_to_exclude.txt │ └── requires.rb ├── example ├── Gemfile ├── README.md ├── Rakefile ├── opal │ └── user.rb └── spec │ └── user_spec.rb ├── exe └── opal-rspec ├── lib-opal ├── opal-rspec.rb └── opal │ ├── rspec.rb │ └── rspec │ ├── async.rb │ ├── async │ ├── configuration.rb │ ├── example.rb │ ├── example_group.rb │ ├── hooks.rb │ ├── memoized_helpers.rb │ ├── reporter.rb │ └── runner.rb │ ├── autorun.rb │ ├── browser.rb │ ├── browser_early.rb │ ├── default_config.rb │ ├── fixes.rb │ ├── fixes │ ├── diff-lcs.rb │ ├── diff-lcs │ │ ├── hunk.rb │ │ └── lcs.rb │ ├── opal.rb │ ├── rspec.rb │ └── rspec │ │ ├── core.rb │ │ ├── core │ │ ├── configuration.rb │ │ ├── example_status_persister.rb │ │ ├── formatters.rb │ │ ├── formatters │ │ │ ├── deprecation_formatter.rb │ │ │ ├── exception_presenter.rb │ │ │ ├── loader.rb │ │ │ ├── snippet_extractor.rb │ │ │ └── syntax_highlighter.rb │ │ ├── metadata.rb │ │ ├── notifications.rb │ │ ├── notifications │ │ │ └── examples_notification.rb │ │ ├── ordering.rb │ │ └── ordering │ │ │ └── random.rb │ │ ├── example_groups.rb │ │ ├── expectations.rb │ │ ├── matchers.rb │ │ ├── matchers │ │ ├── built_in.rb │ │ ├── built_in │ │ │ ├── base_matcher.rb │ │ │ └── start_and_end_with.rb │ │ └── expecteds_for_multiple_diffs.rb │ │ ├── mocks.rb │ │ ├── mocks │ │ ├── error_generator.rb │ │ └── proxy.rb │ │ ├── support.rb │ │ └── support │ │ ├── differ.rb │ │ ├── encoded_string.rb │ │ ├── formatter_support.rb │ │ ├── ruby_features.rb │ │ └── source.rb │ ├── formatter │ ├── browser_formatter.rb │ ├── document_io.rb │ ├── element.rb │ ├── html_printer.rb │ └── noop_flush_string_io.rb │ ├── pre_require_fixes.rb │ ├── requires.rb │ ├── spec_opts.rb.erb │ └── sprockets_runner.rb.erb ├── lib ├── opal-rspec.rb └── opal │ ├── rspec.rb │ └── rspec │ ├── cached_environment.rb │ ├── configuration_parser.rb │ ├── locator.rb │ ├── project_initializer.rb │ ├── project_initializer │ ├── .rspec-opal │ └── spec-opal │ │ └── spec_helper.rb │ ├── rake_task.rb │ ├── runner.rb │ ├── sprockets.rb │ ├── sprockets_environment.rb │ ├── util.rb │ └── version.rb ├── opal-rspec.gemspec ├── rspec-core └── spec │ ├── files_to_exclude.txt │ ├── filters.rb │ ├── fixes │ ├── missing_constants.rb │ └── shared_examples.rb │ └── requires.rb ├── rspec-expectations └── spec │ ├── files_to_exclude.txt │ ├── filters.rb │ ├── fixes │ ├── missing_constants.rb │ └── shared_examples.rb │ └── requires.rb ├── rspec-mocks └── spec │ ├── files_to_exclude.txt │ ├── filters.rb │ ├── fixes │ ├── no_const_hide.rb │ └── shared_examples.rb │ └── requires.rb ├── rspec-support └── spec │ ├── files_to_exclude.txt │ ├── filters.rb │ ├── fixes │ ├── missing_constants.rb │ └── shared_examples.rb │ └── requires.rb ├── spec-opal-passing ├── spec_helper.rb └── tautology_spec.rb ├── spec-opal-rspec ├── core │ ├── example_group_spec.rb │ ├── failed_example_notification_spec.rb │ ├── hooks_spec.rb │ ├── memoized_helpers_spec.rb │ └── metadata_spec.rb ├── expectations │ ├── be_instance_of_spec.rb │ ├── dsl_spec.rb │ ├── expectation_target_spec.rb │ └── yield_spec.rb └── spec_helper.rb ├── spec-opal ├── after_hooks_spec.rb ├── around_hooks_spec.rb ├── async_spec.rb ├── before_hooks_spec.rb ├── browser-formatter │ └── opal_browser_formatter_spec.rb ├── example_spec.rb ├── matchers_spec.rb ├── mock_spec.rb ├── other │ ├── color_on_by_default_spec.rb │ ├── dummy_spec.rb │ ├── formatter_dependency.rb │ ├── ignored_spec.opal │ └── test_formatter.rb ├── should_syntax_spec.rb ├── skip_pending_spec.rb ├── spec_helper.rb ├── sprockets_runner_js_errors.rb.erb └── subject_spec.rb ├── spec ├── integration │ ├── browser_spec.rb │ ├── browser_spec.ru │ ├── spec_opts_spec.rb │ ├── verify_opal_specs_spec.rb │ ├── verify_other_specs_spec.rb │ └── verify_rspec_specs_spec.rb ├── opal │ └── rspec │ │ ├── browser_formatter_spec.rb │ │ ├── browser_formatter_spec.ru │ │ ├── cached_environment_spec.rb │ │ ├── locator_spec.rb │ │ ├── rake_task_spec.rb │ │ ├── runner_spec.rb │ │ ├── sprockets_environment_spec.rb │ │ └── temp_dir_helper.rb ├── rspec │ ├── filter_processor.rb │ ├── shared │ │ ├── opal │ │ │ ├── fixes │ │ │ │ ├── deprecation_helpers.rb │ │ │ │ └── rspec_helpers.rb │ │ │ └── progress_json_formatter.rb │ │ └── opal_filters.rb │ └── support │ │ ├── capybara.rb │ │ ├── upstream_tests.rb │ │ └── upstream_tests │ │ ├── config.rb │ │ ├── files_to_run.rb │ │ ├── result.rb │ │ └── runner.rb └── spec_helper.rb ├── stubs ├── cgi │ ├── escape.rb │ └── util.rb ├── coderay.rb ├── drb │ ├── acl.rb │ └── drb.rb ├── erb │ └── version.rb ├── minitest.rb ├── minitest │ ├── assertions.rb │ └── unit.rb ├── mutex_m.rb ├── open3.rb ├── psych.rb ├── ripper.rb ├── rubygems │ ├── bundler_version_finder.rb │ └── util.rb ├── socket.rb ├── tempfile.rb ├── test │ └── unit │ │ └── assertions.rb └── thread_order.rb └── tasks ├── building.rake └── testing.rake /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | exclude_paths: 2 | - rspec/**/* 3 | - rspec-core/**/* 4 | - rspec-expectations/**/* 5 | - rspec-mocks/**/* 6 | - rspec-support/**/* 7 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: ubuntu 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - "*-stable" 8 | - "*/ci-check" 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | build: 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | os: [ 'ubuntu-latest' ] 19 | ruby: [ ruby-head, 3.2, 3.1, "3.0", 2.7 ] 20 | opal: [ master, 1.7, 1.8 ] 21 | 22 | runs-on: ${{ matrix.os }} 23 | 24 | env: 25 | OPAL_VERSION: ${{ matrix.opal }} 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | with: 30 | submodules: true 31 | - uses: ruby/setup-ruby@v1 32 | with: 33 | ruby-version: ${{ matrix.ruby }} 34 | - name: Setup project 35 | run: bin/setup 36 | - name: Run opal-rspec test 37 | run: "bundle exec exe/opal-rspec spec-opal-passing" 38 | - name: Run rspec test 39 | run: bundle exec rspec 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Gemfile.lock 3 | *.gem 4 | Gemfile.lock 5 | gemfiles/*.lock 6 | /tmp 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rspec"] 2 | path = rspec/upstream 3 | url = https://github.com/rspec/rspec-metagem.git 4 | [submodule "rspec-support"] 5 | path = rspec-support/upstream 6 | url = https://github.com/rspec/rspec-support.git 7 | [submodule "rspec-core"] 8 | path = rspec-core/upstream 9 | url = https://github.com/rspec/rspec-core.git 10 | [submodule "rspec-mocks"] 11 | path = rspec-mocks/upstream 12 | url = https://github.com/rspec/rspec-mocks.git 13 | [submodule "rspec-expectations"] 14 | path = rspec-expectations/upstream 15 | url = https://github.com/rspec/rspec-expectations.git 16 | [submodule "diff-lcs/upstream"] 17 | path = diff-lcs/upstream 18 | url = https://github.com/halostatue/diff-lcs 19 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | -c 2 | -f d 3 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | lib/**/*.rb 2 | opal/**/*.rb 3 | --markup markdown 4 | - 5 | CHANGELOG.md 6 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise 'opal-0.11' do 2 | gem 'opal', '~> 0.11.0' 3 | gem 'opal-sprockets' 4 | end 5 | 6 | appraise 'opal-master' do 7 | gem 'opal', git: 'https://github.com/opal/opal.git' 8 | gem 'opal-sprockets', github: 'opal/opal-sprockets' 9 | end 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Opal-RSpec Changelog 2 | 3 | ## 1.1.0.alpha3 - 2023-09-26 4 | 5 | - Refactor glob_to_re into a robust implementation 6 | 7 | - Add `--opal-opt` CLI option 8 | 9 | - Add `--rbrequire` CLI option 10 | 11 | 12 | ## 1.1.0.alpha2 - 2023-09-20 13 | 14 | - Drop advertised support for Opal v1.6 15 | 16 | - Fix support for Ruby 2.7+ 17 | 18 | - Drop requirement of Opal-Sprockets 19 | 20 | 21 | ## 1.1.0.alpha1 - 2023-09-16 22 | 23 | - Support Opal headless browser runners 24 | 25 | - (Almost) full support for `rspec` CLI util (which we run using `opal-rspec` command) 26 | * Focus works, you can type `opal-rspec spec-opal-passing/tautology_spec.rb:8` to select a given test 27 | * Most of other command line switches of `opal-rspec` work 28 | * You can use a `.rspec-opal` configuration file akin to `.rspec` with the regular RSpec 29 | * 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. 30 | 31 | - Compatibility for upcoming Opal v1.8 32 | 33 | 34 | ## 1.0.0 - 2022-11-24 35 | 36 | - Drop support for anything below Opal v1.6.alpha1 37 | 38 | - Update to the latest RSpec versions 39 | 40 | - Vendor-in `diff-lcs` 41 | 42 | - Rework the async logic to use the `await` feature of Opal 43 | * If you use async features, it's crucial to use a `# await: *await*` magic comment (this will cause any call to a method containing an `await` word to be compiled with an `await` ES8 keyword) 44 | * Both `let` and `subject` that return a promise (ie. are async) must be referenced with an `.await` method 45 | * In `around` blocks, you must call `example.run_await` instead of just `example.run` 46 | * 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) 47 | 48 | - Drop a requirement of `opal-sprockets` 49 | * Sprockets support is still provided, but you need to manually `require "opal/rspec/sprockets"` 50 | 51 | 52 | ## 0.8.0 - 2021-12-01 53 | 54 | - Support for Opal v1.x 55 | 56 | - Fix some x-srting semicolon warnings 57 | 58 | - Fix forwarding the exit code to the rake task 59 | 60 | 61 | ## 0.7.1 - 2019-02-04 62 | 63 | - add a project initializer: `opal-rspec --init` 64 | 65 | 66 | ## 0.7.0 - 2019-02-03 67 | 68 | - Drop support for the legacy async syntax 69 | 70 | - Complete internal overhaul 71 | 72 | - Support for Opal v0.11 73 | 74 | 75 | ## 0.6.1 - 2017-05-25 76 | 77 | - Fixed compatibility with Rake 12 78 | 79 | - Open up to opal 0.11 (not officially supported yet) 80 | 81 | 82 | ## 0.6.0 - 2016-08-29 83 | 84 | - Support for Opal 0.8/0.9 removed 85 | 86 | - Opal 0.10 support 87 | 88 | - Arity checking enabled by default 89 | 90 | - Dropped support for PhantomJS < 2.0 91 | 92 | - Removed `Kernel#caller` monkey patch so test file/line metadata is only available if supplied via test metadata or for failures. Should improve performance since an exception isn't thrown for every test to gather the data 93 | 94 | 95 | ## 0.5.0 - 2015-12-08 96 | 97 | - 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. 98 | 99 | - Update to RSpec 3.1 (core is 3.1.7, expectations/support 3.1.2, mocks 3.1.3) 100 | 101 | - Opal 0.9 compatibility 102 | 103 | - A lot more aspects of RSpec should work now as 20+ Opal pull requests were completed from opal-rspec work 104 | 105 | - Remove copy of source from opal-rspec git repo (and just rely on git submodule fetch) 106 | 107 | - Rake task improvements: 108 | - supports passing a test pattern (include and exclude) and FileLists besides 'spec/**/*_spec.rb 109 | - colors, formatter, and additional requires can be supplied from the command line via the SPEC_OPTS environment variable 110 | 111 | - Formatters: 112 | - Fixed issues with RSpec's BaseTextFormatter and made ProgressFormatter the default when run via the Rake task 113 | - Fix redundant messages with expectation fails 114 | - 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 115 | - JSON formatter supported 116 | 117 | - Fixed issues with constants/example group naming 118 | 119 | - Basic nodejs runner support 120 | 121 | - A lot more matchers enabled 122 | 123 | * PhantomJS 2.0 compatibility (also still compatible with 1.9.8). Thanks to @aost. Closes out https://github.com/opal/opal-rspec/issues/42 124 | 125 | 126 | ## 0.4.3 - 2015-06-14 127 | 128 | - Allow the gem to be run under Opal 0.7 and 0.8 129 | - Fix some threading issues 130 | - Avoid some other calls to mutable-strings methods 131 | 132 | 133 | ## 0.4.2 - 2015-03-28 134 | 135 | - Avoid phantomjs warning messages 136 | 137 | 138 | ## 0.4.1 - 2015-02-25 139 | 140 | - Remove predicate matcher fixes as Opal supports $1..$9 special gvars. 141 | 142 | - Update Opal dependency for ~> 0.6.0. 143 | 144 | - Remove double-escaping in inline x-strings (from Opal bug fix). 145 | 146 | - Remove opal-sprockets dependency - build tools now part of opal. 147 | 148 | - Replaced browser formatter to use html printer from rspec 149 | 150 | - Add timeout support to asynchronous specs 151 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | 4 | unless Dir[__dir__ + '/rspec{,-{core,expectations,mocks,support}}/upstream'].any? 5 | raise 'Run: "git submodule update --init" to get RSpec sources' 6 | end 7 | 8 | # These need to come from our local path in order for create_requires.rb to work properly 9 | gem 'rspec', path: 'rspec/upstream' 10 | gem 'rspec-support', path: 'rspec-support/upstream' 11 | gem 'rspec-core', path: 'rspec-core/upstream' 12 | gem 'rspec-mocks', path: 'rspec-mocks/upstream' 13 | gem 'rspec-expectations', path: 'rspec-expectations/upstream' 14 | 15 | gem 'pry' 16 | 17 | case ENV['OPAL_VERSION'] 18 | when 'local' 19 | gem 'opal', path: '../opal' 20 | when /^[0-9]/ 21 | gem 'opal', "~> #{ENV['OPAL_VERSION']}.0a" 22 | when String 23 | gem 'opal', git: 'https://github.com/opal/opal.git', branch: ENV['OPAL_VERSION'] 24 | end 25 | 26 | gem 'opal-sprockets', '>=1.0' 27 | 28 | gem 'puma' 29 | gem 'rack', '<3' 30 | gem 'base64' 31 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler.require 3 | 4 | import 'tasks/testing.rake' 5 | import 'tasks/building.rake' 6 | 7 | task :default => [:rspec] 8 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "opal/rspec" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /bin/generate_requires: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ROOT = "#{__dir__}/.." 4 | $LOAD_PATH.unshift "#{ROOT}/rspec/upstream/lib" 5 | $LOAD_PATH.unshift "#{ROOT}/rspec-core/upstream/lib" 6 | $LOAD_PATH.unshift "#{ROOT}/rspec-expectations/upstream/lib" 7 | $LOAD_PATH.unshift "#{ROOT}/rspec-mocks/upstream/lib" 8 | $LOAD_PATH.unshift "#{ROOT}/rspec-support/upstream/lib" 9 | 10 | require 'json' 11 | require 'pathname' 12 | 13 | # Opal will not have the built-in RNG, which affects the required outcome 14 | Object.send(:remove_const, :Random) 15 | 16 | # These scripts allow a leaner top level spec (like noted here) 17 | BASE_FILES = %w{rspec rspec/mocks rspec/expectations rspec/core rspec/core/mocking_adapters/rspec} 18 | FORMATTERS = %w{base_formatter base_text_formatter progress_formatter documentation_formatter html_printer json_formatter}.map {|f| "rspec/core/formatters/#{f}"} 19 | MATCHERS = Dir.glob('rspec-expectations/upstream/lib/rspec/matchers/built_in/**/*.rb').map do |each_file| 20 | path = Pathname.new(each_file).relative_path_from(Pathname.new('rspec-expectations/upstream/lib')).to_s 21 | path.sub File.extname(path), '' 22 | end 23 | MOCK_STUFF = %w{matchers/expectation_customization any_instance}.map { |f| "rspec/mocks/#{f}" } 24 | REQUIRES = BASE_FILES + FORMATTERS + MATCHERS + MOCK_STUFF 25 | 26 | # Should not need to edit below this 27 | 28 | ROOTS = Dir[__dir__+'/../rspec{,-{core,expectations,mocks,support}}/upstream/lib'].map {|root| File.expand_path(root)} 29 | ROOTS_REGEXP = /\A(#{ROOTS.map {|r| Regexp.escape r}.join('|')})\// 30 | 31 | module Kernel 32 | alias :require_before_opal_rspec :require 33 | def require path 34 | result = require_before_opal_rspec(path) 35 | puts "requiring: #{path} (#{result})" 36 | RSPEC_PATHS << path 37 | result 38 | end 39 | 40 | alias :require_relative_before_opal_rspec :require_relative 41 | def require_relative path 42 | base = File.dirname(caller(1,1).first) 43 | path_for_require = File.expand_path(path, base).sub(ROOTS_REGEXP, '') 44 | require path_for_require 45 | end 46 | end 47 | 48 | RSPEC_PATHS = [] 49 | REQUIRES.each {|r| require r } 50 | 51 | # Put top level items first 52 | requires = RSPEC_PATHS.uniq.sort 53 | 54 | File.open "#{ROOT}/lib-opal/opal/rspec/requires.rb", 'w' do |file| 55 | file.puts "# Generated automatically by #{$0}" 56 | requires.each do |path| 57 | file.puts "require '#{path}'" 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /bin/opal: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | exec 'bundle', 'exec', 'opal', *ARGV 4 | -------------------------------------------------------------------------------- /bin/opal-rspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | exec 'bundle', 'exec', 'opal-rspec', *ARGV 4 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | git submodule update --init 7 | bundle install 8 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require 'opal/rspec' 2 | require 'opal/sprockets/server' 3 | 4 | Opal::Config.source_map_enabled = false 5 | Opal::Config.arity_check_enabled = true 6 | 7 | sprockets_env = Opal::RSpec::SprocketsEnvironment.new(spec_pattern = 'spec-opal/**/*_spec.{rb,opal}', 8 | spec_exclude_pattern = nil, 9 | spec_files = nil, 10 | default_path = 'spec-opal') 11 | 12 | sprockets_env.cache = ::Sprockets::Cache::FileStore.new(File.join('tmp', 'cache', 'opal_specs')) 13 | run Opal::Sprockets::Server.new(sprockets: sprockets_env) { |s| 14 | s.main = 'sprockets_runner_js_errors' 15 | # sprockets_runner_js_errors will not be in the opal load path by default 16 | s.append_path 'spec/integration/rack' 17 | sprockets_env.add_spec_paths_to_sprockets 18 | s.debug = ENV['OPAL_DEBUG'] 19 | } 20 | -------------------------------------------------------------------------------- /diff-lcs/spec/files_to_exclude.txt: -------------------------------------------------------------------------------- 1 | **/ldiff_spec.rb 2 | -------------------------------------------------------------------------------- /diff-lcs/spec/requires.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.add_formatter RSpec::Core::Formatters::JsonFormatter, File.open('/tmp/diff-lcs-results.json', 'w') 3 | config.add_formatter RSpec::Core::Formatters::ProgressFormatter, $stdout 4 | end 5 | -------------------------------------------------------------------------------- /example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake' 4 | gem 'opal-rspec', path: '../' 5 | # gem 'opal', '~> 0.11.0.rc1' 6 | gem 'opal', path: '../../opal' 7 | gem 'opal-sprockets', github: 'opal/opal-sprockets' 8 | 9 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # opal-spec example 2 | 3 | Install dependencies: 4 | 5 | ``` 6 | bundle install 7 | ``` 8 | 9 | Run examples: 10 | 11 | ``` 12 | bundle exec rake 13 | ``` 14 | -------------------------------------------------------------------------------- /example/Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler.require 3 | 4 | # Add our opal/ directory to the load path 5 | Opal.append_path File.expand_path('../opal', __FILE__) 6 | 7 | require 'opal/rspec/rake_task' 8 | Opal::RSpec::RakeTask.new(:default) 9 | -------------------------------------------------------------------------------- /example/opal/user.rb: -------------------------------------------------------------------------------- 1 | class User 2 | attr_accessor :name 3 | 4 | def initialize(name) 5 | @name = name 6 | end 7 | 8 | def admin? 9 | @name == 'Bob' 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /example/spec/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'user' 2 | 3 | describe User do 4 | it '#initialize accepts a name' do 5 | expect(User.new('Jim').name).to eq('Jim') 6 | end 7 | 8 | it 'is an admin if name is Bob' do 9 | expect(User.new('Bob')).to be_admin 10 | end 11 | 12 | it 'is not an admin if name is not Bob' do 13 | expect(User.new('Jim')).to_not be_admin 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /exe/opal-rspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Error codes are taken from /usr/include/sysexits.h 4 | 5 | require 'rake' 6 | require 'opal/rspec/runner' 7 | require 'opal/rspec/configuration_parser' 8 | 9 | require 'shellwords' 10 | 11 | parser = Opal::RSpec::Core::Parser.new(ARGV) 12 | options = parser.parse 13 | 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 19 | end 20 | 21 | runner.run 22 | else 23 | exit options[:runner].(ARGV, $stderr, $stdout) 24 | end 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib-opal/opal-rspec.rb: -------------------------------------------------------------------------------- 1 | require 'opal/rspec' 2 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec.rb: -------------------------------------------------------------------------------- 1 | require 'js' 2 | 3 | require 'opal/rspec/browser_early' if JS[:document] 4 | require 'opal/rspec/pre_require_fixes' 5 | require 'opal/rspec/requires' 6 | require 'opal/rspec/fixes' 7 | require 'opal/rspec/default_config' 8 | require 'opal/rspec/browser' if JS[:document] 9 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/async.rb: -------------------------------------------------------------------------------- 1 | require 'await' 2 | 3 | require 'opal/rspec/async/example' 4 | require 'opal/rspec/async/example_group' 5 | require 'opal/rspec/async/memoized_helpers' 6 | require 'opal/rspec/async/hooks' 7 | require 'opal/rspec/async/reporter' 8 | require 'opal/rspec/async/runner' 9 | require 'opal/rspec/async/configuration' 10 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/async/configuration.rb: -------------------------------------------------------------------------------- 1 | # await: *await* 2 | 3 | module RSpec 4 | module Core 5 | class Configuration 6 | def with_suite_hooks_await 7 | return yield if dry_run? 8 | 9 | begin 10 | RSpec.current_scope = :before_suite_hook 11 | run_suite_hooks("a `before(:suite)` hook", @before_suite_hooks) 12 | yield.await 13 | ensure 14 | RSpec.current_scope = :after_suite_hook 15 | run_suite_hooks("an `after(:suite)` hook", @after_suite_hooks) 16 | RSpec.current_scope = :suite 17 | end 18 | end 19 | 20 | def run_suite_hooks_await(hook_description, hooks) 21 | context = SuiteHookContext.new(hook_description, reporter) 22 | 23 | hooks.each do |hook| 24 | begin 25 | hook.run(context).await 26 | rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex 27 | context.set_exception(ex) 28 | 29 | # Do not run subsequent `before` hooks if one fails. 30 | # But for `after` hooks, we run them all so that all 31 | # cleanup bits get a chance to complete, minimizing the 32 | # chance that resources get left behind. 33 | break if hooks.equal?(@before_suite_hooks) 34 | end 35 | end 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/async/example.rb: -------------------------------------------------------------------------------- 1 | # await: *await* 2 | 3 | module RSpec 4 | module Core 5 | class Example 6 | def run_await(example_group_instance, reporter) 7 | # added awaits 8 | @example_group_instance = example_group_instance 9 | @reporter = reporter 10 | RSpec.configuration.configure_example(self, hooks) 11 | RSpec.current_example = self 12 | 13 | start(reporter) 14 | Pending.mark_pending!(self, pending) if pending? 15 | 16 | begin 17 | if skipped? 18 | Pending.mark_pending! self, skip 19 | elsif !RSpec.configuration.dry_run? 20 | with_around_and_singleton_context_hooks_await do 21 | begin 22 | run_before_example_await 23 | RSpec.current_scope = :example 24 | @example_group_instance.instance_exec_await(self, &@example_block) 25 | 26 | if pending? 27 | Pending.mark_fixed! self 28 | 29 | raise Pending::PendingExampleFixedError, 30 | 'Expected example to fail since it is pending, but it passed.', 31 | [location] 32 | end 33 | rescue Pending::SkipDeclaredInExample => _ 34 | # The "=> _" is normally useless but on JRuby it is a workaround 35 | # for a bug that prevents us from getting backtraces: 36 | # https://github.com/jruby/jruby/issues/4467 37 | # 38 | # no-op, required metadata has already been set by the `skip` 39 | # method. 40 | rescue AllExceptionsExcludingDangerousOnesOnRubiesThatAllowIt => e 41 | set_exception(e) 42 | ensure 43 | RSpec.current_scope = :after_example_hook 44 | run_after_example_await 45 | end 46 | end 47 | end 48 | rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e 49 | set_exception(e) 50 | ensure 51 | @example_group_instance = nil # if you love something... let it go 52 | end 53 | 54 | finish(reporter) 55 | ensure 56 | execution_result.ensure_timing_set(clock) 57 | RSpec.current_example = nil 58 | end 59 | 60 | def run_before_example_await 61 | # added awaits 62 | @example_group_instance.setup_mocks_for_rspec 63 | hooks.run_await(:before, :example, self) 64 | end 65 | 66 | def run_after_example_await 67 | # added awaits 68 | assign_generated_description if defined?(::RSpec::Matchers) 69 | hooks.run_await(:after, :example, self) 70 | verify_mocks 71 | ensure 72 | @example_group_instance.teardown_mocks_for_rspec 73 | end 74 | 75 | def with_around_and_singleton_context_hooks_await 76 | singleton_context_hooks_host = example_group_instance.singleton_class 77 | singleton_context_hooks_host.run_before_context_hooks_await(example_group_instance) 78 | with_around_example_hooks_await { yield.await } 79 | ensure 80 | singleton_context_hooks_host.run_after_context_hooks_await(example_group_instance) 81 | end 82 | 83 | def with_around_example_hooks_await 84 | RSpec.current_scope = :before_example_hook 85 | hooks.run_await(:around, :example, self) { yield.await } 86 | rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e 87 | set_exception(e) 88 | end 89 | 90 | def instance_exec_await(*args, &block) 91 | @example_group_instance.instance_exec_await(*args, &block) 92 | end 93 | 94 | class Procsy 95 | alias run_await run 96 | end 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/async/example_group.rb: -------------------------------------------------------------------------------- 1 | # await: *await* 2 | 3 | module ::RSpec 4 | module Core 5 | class ExampleGroup 6 | # @param duration [Integer, Float] time in seconds to wait 7 | def delay(duration, &block) 8 | `setTimeout(block, duration * 1000)` 9 | self 10 | end 11 | 12 | def delay_with_promise(duration, &block) 13 | result = PromiseV2.new 14 | delay(duration) { result.resolve } 15 | result.then(&block) 16 | end 17 | 18 | def self.run_await(reporter=RSpec::Core::NullReporter) 19 | # added awaits 20 | return if RSpec.world.wants_to_quit 21 | reporter.example_group_started(self) 22 | 23 | should_run_context_hooks = descendant_filtered_examples.any? 24 | begin 25 | RSpec.current_scope = :before_context_hook 26 | run_before_context_hooks_await(new('before(:context) hook')) if should_run_context_hooks 27 | result_for_this_group = run_examples_await(reporter) 28 | results_for_descendants = ordering_strategy.order(children).map_await { |child| child.run_await(reporter) }.all? 29 | result_for_this_group && results_for_descendants 30 | rescue Pending::SkipDeclaredInExample => ex 31 | for_filtered_examples(reporter) { |example| example.skip_with_exception(reporter, ex) } 32 | true 33 | rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex 34 | for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) } 35 | RSpec.world.wants_to_quit = true if reporter.fail_fast_limit_met? 36 | false 37 | ensure 38 | RSpec.current_scope = :after_context_hook 39 | run_after_context_hooks_await(new('after(:context) hook')) if should_run_context_hooks 40 | reporter.example_group_finished(self) 41 | end 42 | end 43 | 44 | def self.run_examples_await(reporter) 45 | # added awaits 46 | ordering_strategy.order(filtered_examples).map_await do |example| 47 | next if RSpec.world.wants_to_quit 48 | instance = new(example.inspect_output) 49 | set_ivars(instance, before_context_ivars) 50 | succeeded = example.run_await(instance, reporter) 51 | if !succeeded && reporter.fail_fast_limit_met? 52 | RSpec.world.wants_to_quit = true 53 | end 54 | succeeded 55 | end.all? 56 | end 57 | 58 | # @private 59 | def self.run_before_context_hooks_await(example_group_instance) 60 | set_ivars(example_group_instance, superclass_before_context_ivars) 61 | 62 | @currently_executing_a_context_hook = true 63 | 64 | ContextHookMemoized::Before.isolate_for_context_hook_await(example_group_instance) do 65 | hooks.run_await(:before, :context, example_group_instance) 66 | end 67 | ensure 68 | store_before_context_ivars(example_group_instance) 69 | @currently_executing_a_context_hook = false 70 | end 71 | 72 | def self.run_after_context_hooks_await(example_group_instance) 73 | set_ivars(example_group_instance, before_context_ivars) 74 | 75 | @currently_executing_a_context_hook = true 76 | 77 | ContextHookMemoized::After.isolate_for_context_hook_await(example_group_instance) do 78 | hooks.run_await(:after, :context, example_group_instance) 79 | end 80 | ensure 81 | before_context_ivars.clear 82 | @currently_executing_a_context_hook = false 83 | end 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/async/hooks.rb: -------------------------------------------------------------------------------- 1 | # await: *await* 2 | 3 | require 'rspec/core/hooks' 4 | 5 | module RSpec 6 | module Core 7 | module Hooks 8 | class HookCollections 9 | def run_example_hooks_for_await(example, position, each_method) 10 | # WAS: 11 | # owner_parent_groups.__send__(each_method) do |group| 12 | # group.hooks.run_owned_hooks_for(position, :example, example) 13 | # end 14 | case each_method 15 | when :each 16 | groups = owner_parent_groups 17 | when :reverse_each 18 | groups = owner_parent_groups.reverse 19 | else 20 | raise "Unsupported each_method: #{each_method}" 21 | end 22 | groups.each_await do |group| 23 | group.hooks.run_owned_hooks_for_await(position, :example, example) 24 | end 25 | end 26 | 27 | def run_owned_hooks_for_await(position, scope, example_or_group) 28 | # WAS: 29 | # matching_hooks_for(position, scope, example_or_group).each do |hook| 30 | # hook.run(example_or_group) 31 | # end 32 | 33 | matching_hooks_for(position, scope, example_or_group).each_await do |hook| 34 | hook.run_await(example_or_group) 35 | end 36 | end 37 | 38 | def run_await(position, scope, example_or_group) 39 | return if RSpec.configuration.dry_run? 40 | 41 | if scope == :context 42 | unless example_or_group.class.metadata[:skip] 43 | run_owned_hooks_for_await(position, :context, example_or_group) 44 | end 45 | else 46 | case position 47 | when :before then run_example_hooks_for_await(example_or_group, :before, :reverse_each) 48 | when :after then run_example_hooks_for_await(example_or_group, :after, :each) 49 | when :around then run_around_example_hooks_for_await(example_or_group) { yield.await } 50 | end 51 | end 52 | end 53 | 54 | def run_around_example_hooks_for_await(example) 55 | hooks = FlatMap.flat_map(owner_parent_groups) do |group| 56 | group.hooks.matching_hooks_for(:around, :example, example) 57 | end 58 | 59 | return yield if hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy` 60 | 61 | initial_procsy = Example::Procsy.new(example) { yield.await } 62 | hooks.inject(initial_procsy) do |procsy, around_hook| 63 | procsy.wrap { around_hook.execute_with_await(example, procsy) } 64 | end.call.await 65 | end 66 | end 67 | 68 | class BeforeHook < Hook 69 | def run_await(example) 70 | example.instance_exec_await(example, &block) 71 | end 72 | end 73 | 74 | # @private 75 | class AfterHook < Hook 76 | def run_await(example) 77 | example.instance_exec_await(example, &block) 78 | rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex 79 | example.set_exception(ex) 80 | end 81 | end 82 | 83 | # @private 84 | class AfterContextHook < Hook 85 | def run_await(example) 86 | example.instance_exec_await(example, &block) 87 | rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e 88 | RSpec.configuration.reporter.notify_non_example_exception(e, "An error occurred in an `after(:context)` hook.") 89 | end 90 | end 91 | 92 | class AroundHook < Hook 93 | def execute_with_await(example, procsy) 94 | example.instance_exec_await(procsy, &block) 95 | return if procsy.executed? 96 | Pending.mark_skipped!(example, 97 | "#{hook_description} did not execute the example") 98 | end 99 | end 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/async/memoized_helpers.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Core 3 | # This module is included in {ExampleGroup}, making the methods 4 | # available to be called from within example blocks. 5 | # 6 | # @see ClassMethods 7 | module MemoizedHelpers 8 | class ContextHookMemoized 9 | def self.isolate_for_context_hook_await(example_group_instance) 10 | exploding_memoized = self 11 | 12 | example_group_instance.instance_exec_await do 13 | @__memoized = exploding_memoized 14 | 15 | begin 16 | yield.await 17 | ensure 18 | # This is doing a reset instead of just isolating for context hook. 19 | # Really, this should set the old @__memoized back into place. 20 | # 21 | # Caller is the before and after context hooks 22 | # which are both called from self.run 23 | # I didn't look at why it made tests fail, maybe an object was getting reused in RSpec tests, 24 | # if so, then that probably already works, and its the tests that are wrong. 25 | __init_memoized 26 | end 27 | end 28 | end 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/async/reporter.rb: -------------------------------------------------------------------------------- 1 | # await: *await* 2 | 3 | class ::RSpec::Core::Reporter 4 | def report_await(expected_example_count) 5 | # WAS: 6 | # start(expected_example_count) 7 | # begin 8 | # yield self 9 | # ensure 10 | # finish 11 | # end 12 | # NOW: 13 | start(expected_example_count) 14 | begin 15 | yield(self).await 16 | ensure 17 | finish 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/async/runner.rb: -------------------------------------------------------------------------------- 1 | # await: *await* 2 | 3 | module ::RSpec::Core 4 | class Runner 5 | 6 | # Runs the suite of specs and exits the process with an appropriate exit code. 7 | def self.invoke 8 | disable_autorun! 9 | # WAS: 10 | # status = run(ARGV, $stderr, $stdout).to_i 11 | # exit(status) if status != 0 12 | # NOW: 13 | status = run_await(ARGV, $stderr, $stdout).to_i 14 | exit(status) 15 | end 16 | 17 | def self.run_await(args, err=$stderr, out=$stdout) 18 | trap_interrupt 19 | options = ConfigurationOptions.new(args) 20 | 21 | if options.options[:runner] 22 | options.options[:runner].call(options, err, out) 23 | else 24 | new(options).run_await(err, out) 25 | end 26 | end 27 | 28 | def run_await(err, out) 29 | setup(err, out) 30 | return @configuration.reporter.exit_early(exit_code) if RSpec.world.wants_to_quit 31 | 32 | run_specs_await(@world.ordered_example_groups).tap do 33 | persist_example_statuses 34 | end 35 | end 36 | 37 | def run_specs_await(example_groups) 38 | examples_count = @world.example_count(example_groups) 39 | 40 | examples_passed = @configuration.reporter.report_await(examples_count) do |reporter| 41 | @configuration.with_suite_hooks_await do 42 | if examples_count == 0 && @configuration.fail_if_no_examples 43 | return @configuration.failure_exit_code 44 | end 45 | 46 | example_groups.map_await { |g| g.run_await(reporter) }.all? 47 | end 48 | end 49 | 50 | exit_code(examples_passed) 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/autorun.rb: -------------------------------------------------------------------------------- 1 | require 'opal' 2 | require 'opal-rspec' 3 | ::RSpec::Core::Runner.autorun 4 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/browser.rb: -------------------------------------------------------------------------------- 1 | require 'opal/rspec/formatter/browser_formatter' 2 | 3 | RSpec.configure do |config| 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 9 | end 10 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/browser_early.rb: -------------------------------------------------------------------------------- 1 | unless File.respond_to? :read 2 | def File.read(*) 3 | raise Errno::ENOENT 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/default_config.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.default_formatter = ::RSpec::Core::Formatters::ProgressFormatter 3 | 4 | # # Have to do this in 2 places. This will ensure the default formatter gets the right IO, but need to do this here for custom formatters 5 | # # that will be constructed BEFORE Runner.autorun runs (see runner.rb) 6 | # config.output_stream = $stdout 7 | 8 | # This shouldn't be in here, but RSpec uses undef to change this configuration and that doesn't work well enough yet 9 | config.expect_with :rspec do |c| 10 | c.syntax = [:should, :expect] 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes.rb: -------------------------------------------------------------------------------- 1 | require 'encoding' unless Object.const_defined? :Encoding 2 | # Thread usage in core.rb 3 | require 'thread' 4 | require_relative 'fixes/diff-lcs' 5 | require_relative 'fixes/rspec' 6 | require_relative 'fixes/opal' 7 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/diff-lcs.rb: -------------------------------------------------------------------------------- 1 | require_relative 'diff-lcs/lcs' 2 | require_relative 'diff-lcs/hunk' 3 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/diff-lcs/hunk.rb: -------------------------------------------------------------------------------- 1 | require 'diff/lcs/hunk' 2 | 3 | class Diff::LCS::Hunk 4 | 5 | def unified_diff(last = false) 6 | # Calculate item number range. 7 | s = encode("@@ -#{unified_range(:old, last)} +#{unified_range(:new, last)} @@\n") 8 | 9 | # Outlist starts containing the hunk of the old file. Removing an item 10 | # just means putting a '-' in front of it. Inserting an item requires 11 | # getting it from the new file and splicing it in. We splice in 12 | # +num_added+ items. Remove blocks use +num_added+ because splicing 13 | # changed the length of outlist. 14 | # 15 | # We remove +num_removed+ items. Insert blocks use +num_removed+ 16 | # because their item numbers -- corresponding to positions in the NEW 17 | # file -- don't take removed items into account. 18 | lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0 19 | 20 | # standard:disable Performance/UnfreezeString 21 | outlist = @data_old[lo..hi].map { |e| String.new("#{encode(" ")}#{e.chomp}") } 22 | # standard:enable Performance/UnfreezeString 23 | 24 | last_block = blocks[-1] 25 | 26 | if last 27 | old_missing_newline = missing_last_newline?(@data_old) 28 | new_missing_newline = missing_last_newline?(@data_new) 29 | end 30 | 31 | @blocks.each do |block| 32 | block.remove.each do |item| 33 | op = item.action.to_s # - 34 | offset = item.position - lo + num_added 35 | # fixed for Opal: 36 | outlist[offset] = encode(op) + outlist[offset][1..-1] 37 | num_removed += 1 38 | end 39 | 40 | if last && block == last_block && old_missing_newline && !new_missing_newline 41 | outlist << encode('\\ No newline at end of file') 42 | num_removed += 1 43 | end 44 | 45 | block.insert.each do |item| 46 | op = item.action.to_s # + 47 | offset = item.position - @start_new + num_removed 48 | outlist[offset, 0] = encode(op) + @data_new[item.position].chomp 49 | num_added += 1 50 | end 51 | end 52 | 53 | outlist << encode('\\ No newline at end of file') if last && new_missing_newline 54 | 55 | s += outlist.join(encode("\n")) 56 | 57 | s 58 | end 59 | 60 | def context_diff(last = false) 61 | s = encode("***************\n") 62 | s += encode("*** #{context_range(:old, ",", last)} ****\n") 63 | r = context_range(:new, ",", last) 64 | 65 | if last 66 | old_missing_newline = missing_last_newline?(@data_old) 67 | new_missing_newline = missing_last_newline?(@data_new) 68 | end 69 | 70 | # Print out file 1 part for each block in context diff format if there 71 | # are any blocks that remove items 72 | lo, hi = @start_old, @end_old 73 | removes = @blocks.reject { |e| e.remove.empty? } 74 | 75 | unless removes.empty? 76 | # standard:disable Performance/UnfreezeString 77 | outlist = @data_old[lo..hi].map { |e| String.new("#{encode(" ")}#{e.chomp}") } 78 | # standard:enable Performance/UnfreezeString 79 | 80 | last_block = removes[-1] 81 | 82 | removes.each do |block| 83 | block.remove.each do |item| 84 | outlist[item.position - lo] = encode(block.op) + outlist[item.position - lo][1..-1] # - or ! 85 | end 86 | 87 | if last && block == last_block && old_missing_newline 88 | outlist << encode('\\ No newline at end of file') 89 | end 90 | end 91 | 92 | s += outlist.join(encode("\n")) + encode("\n") 93 | end 94 | 95 | s += encode("--- #{r} ----\n") 96 | lo, hi = @start_new, @end_new 97 | inserts = @blocks.reject { |e| e.insert.empty? } 98 | 99 | unless inserts.empty? 100 | # standard:disable Performance/UnfreezeString 101 | outlist = @data_new[lo..hi].map { |e| String.new("#{encode(" ")}#{e.chomp}") } 102 | # standard:enable Performance/UnfreezeString 103 | 104 | last_block = inserts[-1] 105 | 106 | inserts.each do |block| 107 | block.insert.each do |item| 108 | outlist[item.position - lo] = encode(block.op) + outlist[item.position - lo][1..-1] # + or ! 109 | end 110 | 111 | if last && block == last_block && new_missing_newline 112 | outlist << encode('\\ No newline at end of file') 113 | end 114 | end 115 | s += outlist.join(encode("\n")) 116 | end 117 | 118 | s 119 | end 120 | 121 | def old_diff(_last = false) 122 | warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1 123 | 124 | block = @blocks[0] 125 | 126 | # Calculate item number range. Old diff range is just like a context 127 | # diff range, except the ranges are on one line with the action between 128 | # them. 129 | s = encode("#{context_range(:old, ",")}#{OLD_DIFF_OP_ACTION[block.op]}#{context_range(:new, ",")}\n") 130 | # If removing anything, just print out all the remove lines in the hunk 131 | # which is just all the remove lines in the block. 132 | unless block.remove.empty? 133 | @data_old[@start_old..@end_old].each { |e| s += encode("< ") + e.chomp + encode("\n") } 134 | end 135 | 136 | s += encode("---\n") if block.op == "!" 137 | 138 | unless block.insert.empty? 139 | @data_new[@start_new..@end_new].each { |e| s += encode("> ") + e.chomp + encode("\n") } 140 | end 141 | 142 | s 143 | end 144 | 145 | 146 | def ed_diff(format, _last = false) 147 | warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1 148 | 149 | s = 150 | if format == :reverse_ed 151 | encode("#{ED_DIFF_OP_ACTION[@blocks[0].op]}#{context_range(:old, ",")}\n") 152 | else 153 | encode("#{context_range(:old, " ")}#{ED_DIFF_OP_ACTION[@blocks[0].op]}\n") 154 | end 155 | 156 | unless @blocks[0].insert.empty? 157 | @data_new[@start_new..@end_new].each do |e| 158 | s += e.chomp + encode("\n") 159 | end 160 | s += encode(".\n") 161 | end 162 | s 163 | end 164 | end 165 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/diff-lcs/lcs.rb: -------------------------------------------------------------------------------- 1 | require 'diff/lcs' 2 | 3 | class << Diff::LCS 4 | # mutable strings 5 | def patch(src, patchset, direction = nil) 6 | # Normalize the patchset. 7 | has_changes, patchset = Diff::LCS::Internals.analyze_patchset(patchset) 8 | 9 | return src.respond_to?(:dup) ? src.dup : src unless has_changes 10 | 11 | string = src.is_a?(String) 12 | # Start with a new empty type of the source's class 13 | res = src.class.new 14 | 15 | res = [] if string 16 | 17 | direction ||= Diff::LCS::Internals.intuit_diff_direction(src, patchset) 18 | 19 | ai = bj = 0 20 | 21 | patch_map = PATCH_MAP[direction] 22 | 23 | patchset.each do |change| 24 | # Both Change and ContextChange support #action 25 | action = patch_map[change.action] 26 | 27 | case change 28 | when Diff::LCS::ContextChange 29 | case direction 30 | when :patch 31 | el = change.new_element 32 | op = change.old_position 33 | np = change.new_position 34 | when :unpatch 35 | el = change.old_element 36 | op = change.new_position 37 | np = change.old_position 38 | end 39 | 40 | case action 41 | when "-" # Remove details from the old string 42 | while ai < op 43 | res << (string ? src[ai, 1] : src[ai]) 44 | ai += 1 45 | bj += 1 46 | end 47 | ai += 1 48 | when "+" 49 | while bj < np 50 | res << (string ? src[ai, 1] : src[ai]) 51 | ai += 1 52 | bj += 1 53 | end 54 | 55 | res << el 56 | bj += 1 57 | when "=" 58 | # This only appears in sdiff output with the SDiff callback. 59 | # Therefore, we only need to worry about dealing with a single 60 | # element. 61 | res << el 62 | 63 | ai += 1 64 | bj += 1 65 | when "!" 66 | while ai < op 67 | res << (string ? src[ai, 1] : src[ai]) 68 | ai += 1 69 | bj += 1 70 | end 71 | 72 | bj += 1 73 | ai += 1 74 | 75 | res << el 76 | end 77 | when Diff::LCS::Change 78 | case action 79 | when "-" 80 | while ai < change.position 81 | res << (string ? src[ai, 1] : src[ai]) 82 | ai += 1 83 | bj += 1 84 | end 85 | ai += 1 86 | when "+" 87 | while bj < change.position 88 | res << (string ? src[ai, 1] : src[ai]) 89 | ai += 1 90 | bj += 1 91 | end 92 | 93 | bj += 1 94 | 95 | res << change.element 96 | end 97 | end 98 | end 99 | 100 | while ai < src.size 101 | res << (string ? src[ai, 1] : src[ai]) 102 | ai += 1 103 | bj += 1 104 | end 105 | 106 | if string 107 | res.join 108 | else 109 | res 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/opal.rb: -------------------------------------------------------------------------------- 1 | require 'opal/platform' 2 | require 'opal-parser' 3 | require 'thread' 4 | require 'corelib/marshal' 5 | require 'ruby2_keywords' 6 | 7 | class IO 8 | def closed? 9 | true 10 | end 11 | end 12 | 13 | Errno::ENOTDIR = Class.new(SystemCallError) 14 | 15 | require 'nodejs' if OPAL_PLATFORM == 'nodejs' 16 | 17 | module Kernel 18 | def trap(sig, &block) 19 | end 20 | end 21 | 22 | require 'js' 23 | 24 | module Opal 25 | module RSpec 26 | module Compatibility 27 | module ModuleCase 28 | end 29 | 30 | module ModuleCase2 31 | include ModuleCase 32 | end 33 | 34 | class ModuleCase3 35 | include ModuleCase2 36 | end 37 | 38 | # not currently needed but is referenced in space.rb fix, https://github.com/opal/opal/issues/1279 - fixed in 0.10 39 | def self.module_case_works_right? 40 | instance = ModuleCase3.new 41 | ModuleCase === instance && instance.kind_of?(ModuleCase) 42 | end 43 | 44 | module MultModSuper1 45 | def stuff 46 | :howdy 47 | end 48 | end 49 | 50 | module MultModSuper2 51 | def stuff 52 | super 53 | end 54 | end 55 | 56 | module MultModSuper3 57 | include MultModSuper1 58 | include MultModSuper2 59 | end 60 | 61 | class MultModSuperClass 62 | include MultModSuper3 63 | end 64 | 65 | # https://github.com/opal/opal/issues/568 - still not fixed 66 | def self.multiple_module_include_super_works_right? 67 | MultModSuperClass.new.stuff == :howdy 68 | rescue Exception => _ 69 | false 70 | end 71 | end 72 | end 73 | end 74 | 75 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'rspec/core' 2 | require_relative 'rspec/support' 3 | require_relative 'rspec/expectations' 4 | require_relative 'rspec/matchers' 5 | require_relative 'rspec/mocks' 6 | require_relative 'rspec/example_groups' 7 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core.rb: -------------------------------------------------------------------------------- 1 | require_relative 'core/formatters' 2 | require_relative 'core/notifications' 3 | require_relative 'core/ordering' 4 | require_relative 'core/metadata' 5 | require_relative 'core/configuration' 6 | require_relative 'core/example_status_persister' 7 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/configuration.rb: -------------------------------------------------------------------------------- 1 | # backtick_javascript: true 2 | 3 | require 'rspec/core/configuration' 4 | 5 | module ::RSpec; module Core; class Configuration 6 | def files_or_directories_to_run=(*files) 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 15 | @files_to_run = nil 16 | end 17 | 18 | def requires=(paths) 19 | # can't change requires @ this stage, this method calls RubyProject which will crash on Opal 20 | 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_expand_alternatives(glob) 27 | # If there are no braces, just return the string as an array 28 | return [glob] unless glob =~ /(.*)\{([^\{\}]*?)\}(.*)/ 29 | 30 | prefix, contents, suffix = $1, $2, $3 31 | 32 | alternatives = contents.split(',') 33 | 34 | expanded_patterns = alternatives.map do |alternative| 35 | "#{prefix}#{alternative}#{suffix}" 36 | end 37 | 38 | # Recursively expand for the rest of the pattern 39 | return expanded_patterns.flat_map { |pattern| glob_to_re_expand_alternatives(pattern) } 40 | end 41 | 42 | def glob_to_re(path, pattern) 43 | pattern = remove_ruby_ext(pattern) 44 | if pattern.start_with?(path) 45 | path = "" 46 | else 47 | path += "/" unless path.end_with?("/") 48 | end 49 | pattern = path + pattern 50 | patterns = glob_to_re_expand_alternatives(pattern) 51 | re = patterns.map { |i| Regexp.escape(i) }.join("|").then { |i| "(?:#{i})" } 52 | re = re.gsub('\/\*\*\/', '(?:/|/.*?/)') 53 | .gsub('\*', '[^/]*?') 54 | .gsub('\?', '[^/]') 55 | re = '(?:^|/)' + re + "$" 56 | # Strip multiple '/'s 57 | re = re.gsub(%r{(\\/|/)+}, '/') 58 | # Strip the `/./` 59 | re = re.gsub('/\./', '/') 60 | re = re.gsub('(?:^|/)\./', '(?:^|/)') 61 | Regexp.new(re) 62 | end 63 | 64 | # Only load from loaded files 65 | def get_matching_files(path, pattern) 66 | if pattern.is_a?(Array) 67 | return pattern.map { |pat| get_matching_files(path, pat) }.flatten.sort.uniq 68 | end 69 | `Object.keys(Opal.modules)`.grep(glob_to_re(path, pattern)).sort 70 | end 71 | 72 | # A crude logic to check if a path is a directory perhaps... 73 | # This ought to work in places where we don't have a filesystem. 74 | def is_directory?(path) 75 | return true if path.end_with? '/' 76 | # This is passed with ":" if we run something like: 77 | # opal-rspec spec-opal-passing/tautology_spec.rb:8 78 | return false if path =~ /\[[0-9:]+\]$|:[0-9]+$/ 79 | # Ruby files are certainly not directories 80 | return false if ['.rb', '.opal'].any? { |i| path.end_with? i } 81 | # Otherwise, let's check for modules 82 | !`Object.keys(Opal.modules)`.any? { |i| i.end_with?("/"+remove_ruby_ext(path)) } 83 | end 84 | 85 | def get_files_to_run(paths) 86 | files = paths_to_check(paths).flat_map do |path| 87 | path = path.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR 88 | is_directory?(path) ? gather_directories(path) : extract_location(path) 89 | end.uniq 90 | 91 | return files unless only_failures? 92 | relative_files = files.map { |f| Metadata.relative_path(File.expand_path f) } 93 | intersection = (relative_files & spec_files_with_failures.to_a) 94 | intersection.empty? ? files : intersection 95 | end 96 | 97 | def opal_special_load(file) 98 | file = remove_ruby_ext(file) 99 | long_file = `Object.keys(Opal.modules)`.find { |i| i.end_with?(file) } 100 | `Opal.modules[file] = Opal.modules[long_file]` if long_file 101 | 102 | # Let's try a normalized load 103 | `Opal.load_normalized(file)` 104 | rescue LoadError 105 | # Otherwise, a regular require 106 | require file 107 | end 108 | 109 | alias load_file_handling_errors_before_opal load_file_handling_errors 110 | 111 | def load_file_handling_errors(method, file) 112 | load_file_handling_errors_before_opal(:opal_special_load, file) 113 | end 114 | end; end; end 115 | 116 | class ::RSpec::Core::ConfigurationOptions 117 | # Opal-RSpec should work without need of filesystem 118 | # access, therefore we can't support access to the 119 | # options file. 120 | def options_file_as_erb_string(path) 121 | # ERB.new(File.read(path), nil, '-').result(binding) 122 | # ERB.new(File.read(path), nil, '-') 123 | '' 124 | end 125 | 126 | # Pass command line options directly 127 | def command_line_options 128 | $rspec_opts || {} 129 | end 130 | end 131 | 132 | # Set the default path to spec-opal, to be overwritten 133 | # later. 134 | RSpec.configuration.default_path = "spec-opal" 135 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/example_status_persister.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Core 3 | # Persists example ids and their statuses so that we can filter 4 | # to just the ones that failed the last time they ran. 5 | # @private 6 | class ExampleStatusPersister 7 | def persist 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/formatters.rb: -------------------------------------------------------------------------------- 1 | require_relative 'formatters/deprecation_formatter' 2 | require_relative 'formatters/loader' 3 | require_relative 'formatters/exception_presenter' 4 | require_relative 'formatters/syntax_highlighter' 5 | require_relative 'formatters/snippet_extractor' 6 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/formatters/deprecation_formatter.rb: -------------------------------------------------------------------------------- 1 | class RSpec::Core::Formatters::DeprecationFormatter 2 | # GeneratedDeprecationMessage is a Struct 3 | GeneratedDeprecationMessage.class_eval do 4 | def to_s 5 | msg = String.new("#{@data.deprecated} is deprecated.") 6 | msg += " Use #{@data.replacement} instead." if @data.replacement 7 | msg += " Called from #{@data.call_site}." if @data.call_site 8 | msg 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/formatters/exception_presenter.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Core 3 | module Formatters 4 | class ExceptionPresenter 5 | def indent_lines(lines, failure_number) 6 | alignment_basis = ' ' * @indentation 7 | alignment_basis += "#{failure_number}) " if failure_number 8 | indentation = ' ' * alignment_basis.length 9 | 10 | lines.each_with_index.map do |line, index| 11 | if index == 0 12 | "#{alignment_basis}#{line}" 13 | elsif line.empty? 14 | line 15 | else 16 | "#{indentation}#{line}" 17 | end 18 | end 19 | end 20 | 21 | def fully_formatted(failure_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes) 22 | lines = fully_formatted_lines(failure_number, colorizer) 23 | lines.join("\n") + "\n" 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/formatters/loader.rb: -------------------------------------------------------------------------------- 1 | class ::RSpec::Core::Formatters::Loader 2 | def underscore(camel_cased_word) 3 | # string mutation 4 | word = camel_cased_word.to_s.dup 5 | word = word.gsub(/::/, '/') 6 | word = word.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') 7 | word = word.gsub(/([a-z\d])([A-Z])/, '\1_\2') 8 | word = word.tr("-", "_") 9 | word.downcase 10 | end 11 | end 12 | 13 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/formatters/snippet_extractor.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Core 3 | module Formatters 4 | # @private 5 | class SnippetExtractor 6 | def self.source_from_file(path) 7 | # Don't check for file existence, we may still have its embedded source 8 | # raise NoSuchFileError unless File.exist?(path) 9 | RSpec.world.source_from_file(path) 10 | rescue Errno::ENOENT, Errno::ENAMETOOLONG 11 | # But if we really don't, well... 12 | raise NoSuchFileError 13 | end 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/formatters/syntax_highlighter.rb: -------------------------------------------------------------------------------- 1 | require 'opal-replutils' 2 | 3 | module RSpec 4 | module Core 5 | module Formatters 6 | # @private 7 | # Provides terminal syntax highlighting of code snippets 8 | # when coderay is available. 9 | class SyntaxHighlighter 10 | # A poor-man highlighter 11 | def highlight(lines) 12 | REPLUtils::ColorPrinter.colorize(lines.join("\n")).split("\n") 13 | end 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/metadata.rb: -------------------------------------------------------------------------------- 1 | module ::RSpec::Core::Metadata 2 | class HashPopulator 3 | def populate_location_attributes 4 | backtrace = user_metadata.delete(:caller) 5 | 6 | file_path, line_number = if backtrace 7 | file_path_and_line_number_from(backtrace) 8 | elsif block.respond_to?(:source_location) 9 | block.source_location 10 | else 11 | file_path_and_line_number_from(caller) 12 | end 13 | 14 | file_path ||= "" 15 | 16 | relative_file_path = ::RSpec::Core::Metadata.relative_path(file_path) 17 | absolute_file_path = File.expand_path(relative_file_path) 18 | metadata[:file_path] = relative_file_path 19 | metadata[:line_number] = line_number.to_i 20 | metadata[:location] = "#{relative_file_path}:#{line_number}" 21 | metadata[:absolute_file_path] = absolute_file_path 22 | metadata[:rerun_file_path] ||= relative_file_path 23 | metadata[:scoped_id] = build_scoped_id_for(absolute_file_path) 24 | end 25 | 26 | def build_description_from(parent_description=nil, my_description=nil) 27 | return parent_description.to_s unless my_description 28 | return my_description.to_s if parent_description.to_s == '' 29 | separator = description_separator(parent_description, my_description) 30 | # WAS: 31 | # (parent_description.to_s + separator) << my_description.to_s 32 | # NOW: 33 | (parent_description.to_s + separator) + my_description.to_s 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/notifications.rb: -------------------------------------------------------------------------------- 1 | require_relative 'notifications/examples_notification' 2 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/notifications/examples_notification.rb: -------------------------------------------------------------------------------- 1 | module ::RSpec::Core::Notifications 2 | class ExamplesNotification 3 | def fully_formatted_pending_examples(colorizer=::RSpec::Core::Formatters::ConsoleCodes) 4 | formatted = "\nPending: (Failures listed here are expected and do not affect your suite's status)\n".dup 5 | 6 | pending_notifications.each_with_index do |notification, index| 7 | formatted += notification.fully_formatted(index.next, colorizer) 8 | end 9 | 10 | formatted 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/ordering.rb: -------------------------------------------------------------------------------- 1 | require_relative 'ordering/random' 2 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/core/ordering/random.rb: -------------------------------------------------------------------------------- 1 | # backtick_javascript: true 2 | 3 | # Random causes problems that can lock up a browser (see README) 4 | class ::RSpec::Core::Ordering::Random 5 | HIDE_RANDOM_WARNINGS = false 6 | 7 | def initialize(configuration) 8 | `console.warn("Random order is not currently supported by opal-rspec, using default order.")` unless HIDE_RANDOM_WARNINGS 9 | end 10 | 11 | # Identity is usually the default, so borrowing its implementation, this forces 'accidental' random usage down that path 12 | def order(items) 13 | items 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/example_groups.rb: -------------------------------------------------------------------------------- 1 | module RSpec::ExampleGroups 2 | # opal cannot use mutable strings AND opal doesnt support `\A` or `\z` anchors 3 | def self.base_name_for(group) 4 | return "Anonymous" if group.description.empty? 5 | 6 | # convert to CamelCase 7 | name = ' ' + group.description 8 | 9 | # replaced gsub! with name = name.gsub (mutable strings) 10 | name = name.gsub(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) { Regexp.last_match[1].upcase } 11 | 12 | # mutable strings on these 2 13 | name = name.lstrip # Remove leading whitespace 14 | name = name.gsub(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names 15 | 16 | # Ruby requires first const letter to be A-Z. Use `Nested` 17 | # as necessary to enforce that. 18 | # name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1') 19 | # opal-rspec, mutable strings, also substituted in ^ for \A since \A and $ for \z is not supported in JS regex 20 | name = name.gsub(/^([^A-Z]|$)/, 'Nested\1') 21 | 22 | name 23 | end 24 | 25 | # opal cannot use mutable strings 26 | def self.disambiguate(name, const_scope) 27 | return name unless const_defined_on?(const_scope, name) 28 | 29 | # Add a trailing number if needed to disambiguate from an existing constant. 30 | name = name + "_2" 31 | 32 | while const_defined_on?(const_scope, name) 33 | name = name.next 34 | end 35 | 36 | name 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/expectations.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/lib-opal/opal/rspec/fixes/rspec/expectations.rb -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/matchers.rb: -------------------------------------------------------------------------------- 1 | require_relative 'matchers/built_in' 2 | require_relative 'matchers/expecteds_for_multiple_diffs' 3 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/matchers/built_in.rb: -------------------------------------------------------------------------------- 1 | require_relative 'built_in/base_matcher' 2 | require_relative 'built_in/start_and_end_with' 3 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/matchers/built_in/base_matcher.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/matchers/built_in/base_matcher' 2 | 3 | module RSpec 4 | module Matchers 5 | module BuiltIn 6 | class BaseMatcher 7 | # activesupport/lib/active_support/inflector/methods.rb, line 48 8 | # mutable strings fixed for opal 9 | def self.underscore(camel_cased_word) 10 | word = camel_cased_word.to_s.dup 11 | word = word.gsub(/::/, '/') 12 | word = word.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') 13 | word = word.gsub(/([a-z\d])([A-Z])/, '\1_\2') 14 | word = word.tr("-", "_") 15 | word.downcase 16 | end 17 | 18 | def description 19 | desc = EnglishPhrasing.split_words(self.class.matcher_name) 20 | desc += EnglishPhrasing.list(@expected) if defined?(@expected) 21 | desc 22 | end 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/matchers/built_in/start_and_end_with.rb: -------------------------------------------------------------------------------- 1 | module ::RSpec::Matchers::BuiltIn 2 | class StartAndEndWith 3 | def failure_message 4 | msg = super 5 | if @actual_does_not_have_ordered_elements 6 | msg += ", but it does not have ordered elements" 7 | elsif !actual.respond_to?(:[]) 8 | msg += ", but it cannot be indexed using #[]" 9 | end 10 | msg 11 | # string mutation 12 | # super.tap do |msg| 13 | # if @actual_does_not_have_ordered_elements 14 | # msg << ", but it does not have ordered elements" 15 | # elsif !actual.respond_to?(:[]) 16 | # msg << ", but it cannot be indexed using #[]" 17 | # end 18 | # end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/matchers/expecteds_for_multiple_diffs.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Matchers 3 | # @api private 4 | # Handles list of expected values when there is a need to render 5 | # multiple diffs. Also can handle one value. 6 | class ExpectedsForMultipleDiffs 7 | def self.truncated(description) 8 | return description if description.length <= DESCRIPTION_MAX_LENGTH 9 | description[0...DESCRIPTION_MAX_LENGTH - 3] + "..." 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/mocks.rb: -------------------------------------------------------------------------------- 1 | require_relative 'mocks/error_generator' 2 | require_relative 'mocks/proxy' 3 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/mocks/error_generator.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Mocks 3 | class ErrorGenerator 4 | # mutable strings 5 | def error_message(expectation, args_for_multiple_calls) 6 | expected_args = format_args(expectation.expected_args) 7 | actual_args = format_received_args(args_for_multiple_calls) 8 | 9 | if RSpec::Support::RubyFeatures.distincts_kw_args_from_positional_hash? && expected_args == actual_args 10 | expected_hash = expectation.expected_args.last 11 | actual_hash = args_for_multiple_calls.last.last 12 | if Hash === expected_hash && Hash === actual_hash && 13 | (Hash.ruby2_keywords_hash?(expected_hash) != Hash.ruby2_keywords_hash?(actual_hash)) 14 | actual_args += Hash.ruby2_keywords_hash?(actual_hash) ? " (keyword arguments)" : " (options hash)" 15 | expected_args += Hash.ruby2_keywords_hash?(expected_hash) ? " (keyword arguments)" : " (options hash)" 16 | end 17 | end 18 | 19 | message = default_error_message(expectation, expected_args, actual_args) 20 | 21 | if args_for_multiple_calls.one? 22 | diff = diff_message(expectation.expected_args, args_for_multiple_calls.first) 23 | message += "\nDiff:#{diff}" unless diff.strip.empty? 24 | end 25 | 26 | message 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/mocks/proxy.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Mocks 3 | # @private 4 | class Proxy 5 | def ensure_can_be_proxied!(object) 6 | return unless object.is_a?(Symbol) || object.frozen? 7 | return if object.nil? 8 | 9 | msg = "Cannot proxy frozen objects" 10 | if Symbol === object 11 | msg += ". Symbols such as #{object} cannot be mocked or stubbed." 12 | else 13 | msg += ", rspec-mocks relies on proxies for method stubbing and expectations." 14 | end 15 | raise ArgumentError, msg 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/support.rb: -------------------------------------------------------------------------------- 1 | require_relative 'support/encoded_string' 2 | require_relative 'support/formatter_support' 3 | require_relative 'support/differ' 4 | require_relative 'support/ruby_features' 5 | require_relative 'support/source' 6 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/support/differ.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Support 3 | class Differ 4 | def hash_to_string(hash) 5 | formatted_hash = ObjectFormatter.prepare_for_inspection(hash) 6 | formatted_hash.keys.sort_by { |k| k.to_s }.map do |key| 7 | pp_key = PP.singleline_pp(key, []) 8 | pp_value = PP.singleline_pp(formatted_hash[key], []) 9 | 10 | "#{pp_key.join} => #{pp_value.join}," 11 | end.join("\n") 12 | end 13 | 14 | def object_to_string(object) 15 | object = @object_preparer.call(object) 16 | case object 17 | when Hash 18 | hash_to_string(object) 19 | when Array 20 | PP.pp(ObjectFormatter.prepare_for_inspection(object), []).join 21 | when String 22 | object =~ /\n/ ? object : object.inspect 23 | else 24 | PP.pp(object, []).join 25 | end 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/support/encoded_string.rb: -------------------------------------------------------------------------------- 1 | class Encoding::UndefinedConversionError < StandardError; end unless defined? Encoding::UndefinedConversionError 2 | class Encoding::InvalidByteSequenceError < StandardError; end unless defined? Encoding::InvalidByteSequenceError 3 | class Encoding::ConverterNotFoundError < StandardError; end unless defined? Encoding::ConverterNotFoundError 4 | 5 | # Opal doesn't support encoding 6 | class RSpec::Support::EncodedString 7 | def <<(string) 8 | @string += matching_encoding(string) 9 | end 10 | 11 | def self.pick_encoding(source_a, source_b) 12 | "utf-8" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/support/formatter_support.rb: -------------------------------------------------------------------------------- 1 | module FormatterSupport 2 | def run_example_specs_with_formatter(formatter_option) 3 | options = RSpec::Core::ConfigurationOptions.new(%W[spec/rspec/core/resources/formatter_specs.rb --format #{formatter_option} --order defined]) 4 | 5 | err, out = StringIO.new, StringIO.new 6 | err.set_encoding("utf-8") if err.respond_to?(:set_encoding) 7 | 8 | runner = RSpec::Core::Runner.new(options) 9 | configuration = runner.instance_variable_get("@configuration") 10 | configuration.backtrace_formatter.exclusion_patterns << /rspec_with_simplecov/ 11 | configuration.backtrace_formatter.inclusion_patterns = [] 12 | 13 | runner.run(err, out) 14 | 15 | # WAS: 16 | # output = out.string 17 | # output.gsub!(/\d+(?:\.\d+)?(s| seconds)/, "n.nnnn\\1") 18 | # NOW: 19 | output = out.string.gsub(/\d+(?:\.\d+)?(s| seconds)/, "n.nnnn\\1") 20 | 21 | caller_line = RSpec::Core::Metadata.relative_path(caller.first) 22 | output.lines.reject do |line| 23 | # remove the direct caller as that line is different for the summary output backtraces 24 | line.include?(caller_line) || 25 | 26 | # ignore scirpt/rspec_with_simplecov because we don't usually have it locally but 27 | # do have it on travis 28 | line.include?("script/rspec_with_simplecov") || 29 | 30 | # this line varies a bit depending on how you run the specs (via `rake` vs `rspec`) 31 | line.include?('/exe/rspec:') 32 | end.join 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/support/ruby_features.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Support 3 | # Temporary implementation 4 | def self.require_rspec_support(what) 5 | require "rspec/support/#{what}" 6 | end 7 | end 8 | end 9 | 10 | require 'rspec/support/ruby_features' 11 | 12 | module RSpec 13 | module Support 14 | module RubyFeatures 15 | module_function 16 | 17 | def ripper_supported? 18 | false 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/fixes/rspec/support/source.rb: -------------------------------------------------------------------------------- 1 | require 'js' 2 | 3 | module RSpec 4 | module Support 5 | class Source 6 | # Allow to use embedded sources 7 | def self.from_file(path) 8 | source = JS[:Opal].JS[:file_sources].JS[path] 9 | source ||= JS[:Opal].JS[:file_sources].JS["./#{path}"] 10 | source ||= File.read(path) 11 | new(source, path) 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/formatter/browser_formatter.rb: -------------------------------------------------------------------------------- 1 | require_relative 'document_io' 2 | require_relative 'html_printer' 3 | 4 | module Opal 5 | module RSpec 6 | class BrowserFormatter < ::RSpec::Core::Formatters::HtmlFormatter 7 | ::RSpec::Core::Formatters.register self, :example_group_finished 8 | 9 | def initialize(output) 10 | super DocumentIO.new 11 | @printer = Opal::RSpec::HtmlPrinter.new(@output) 12 | end 13 | 14 | def example_group_started(notification) 15 | # Since we hook print_example_group_end, we override this method 16 | @example_group_red = false 17 | @example_group_number += 1 18 | 19 | @printer.print_example_group_start(example_group_number, notification.group.description, notification.group.parent_groups.size) 20 | @printer.flush 21 | end 22 | 23 | def example_group_finished(notification) 24 | @printer.print_example_group_end 25 | end 26 | 27 | def start_dump(_notification) 28 | # Don't need to call "print_example_group_end" like base does since we hook that event 29 | end 30 | 31 | def extra_failure_content(failure) 32 | backtrace = failure.exception.backtrace.map { |line| ::RSpec.configuration.backtrace_formatter.backtrace_line(line) } 33 | # No snippet extractor due to code ray dependency 34 | "
#{backtrace.compact}
" 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/formatter/document_io.rb: -------------------------------------------------------------------------------- 1 | # backtick_javascript: true 2 | 3 | module Opal 4 | module RSpec 5 | class DocumentIO < IO 6 | include IO::Writable if defined? IO::Writable 7 | 8 | def initialize 9 | `document.open()` 10 | end 11 | 12 | def close 13 | @closed = true 14 | `document.close()` 15 | end 16 | 17 | def write(html) 18 | if @closed 19 | `console.error(#{"DOC closed, can't write #{html}" })` 20 | else 21 | `document.write(#{html})` 22 | end 23 | end 24 | 25 | def flush 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/formatter/element.rb: -------------------------------------------------------------------------------- 1 | # backtick_javascript: true 2 | 3 | module Opal 4 | module RSpec 5 | class Element 6 | attr_reader :native 7 | 8 | def self.id(id) 9 | new(`document.getElementById(id)`) 10 | end 11 | 12 | def self.klass(klass) 13 | new(`document.getElementsByClassName(#{klass})[0]`) 14 | end 15 | 16 | def self.from_string(str) 17 | dummy_div = `document.createElement('div')` 18 | `#{dummy_div}.innerHTML = #{str}` 19 | new(`#{dummy_div}.children[0]`) 20 | end 21 | 22 | def initialize(el, attrs={}) 23 | if String === el 24 | @native = `document.createElement(el)` 25 | else 26 | @native = el 27 | end 28 | 29 | attrs.each { |name, val| __send__ "#{name}=", val } 30 | end 31 | 32 | def class_name 33 | `#@native.className` 34 | end 35 | 36 | def get_child_by_tag_name(tag, index=0) 37 | elements = `#@native.getElementsByTagName(#{tag})` 38 | # is an HTMLCollection, not an array 39 | element_array = [] 40 | %x{ 41 | for (var i=0; i < #{elements}.length; i++) { 42 | #{element_array}.push(#{elements}[i]); 43 | } 44 | } 45 | Element.new(element_array[index]) 46 | end 47 | 48 | def class_name=(name) 49 | `#@native.className = #{name}` 50 | end 51 | 52 | def native 53 | `#@native` 54 | end 55 | 56 | def outer_html 57 | `#@native.outerHTML` 58 | end 59 | 60 | def on_click=(lambda) 61 | `#@native.onclick = #{lambda}` 62 | end 63 | 64 | def html=(html) 65 | `#@native.innerHTML = #{html}` 66 | end 67 | 68 | def text=(text) 69 | self.html = text.gsub(//, '>') 70 | end 71 | 72 | def type=(type) 73 | `#@native.type = #{type}` 74 | end 75 | 76 | def append(child) 77 | `#@native.appendChild(#{child.native})` 78 | end 79 | 80 | alias << append 81 | 82 | def css_text=(text) 83 | %x{ 84 | if (#@native.styleSheet) { 85 | #@native.styleSheet.cssText = #{text}; 86 | } 87 | else { 88 | #@native.appendChild(document.createTextNode(#{text})); 89 | } 90 | } 91 | end 92 | 93 | def style(name, value) 94 | `#@native.style[#{name}] = value` 95 | end 96 | 97 | def append_to_head 98 | `document.getElementsByTagName('head')[0].appendChild(#@native)` 99 | end 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/formatter/html_printer.rb: -------------------------------------------------------------------------------- 1 | # backtick_javascript: true 2 | 3 | require_relative 'noop_flush_string_io' 4 | require_relative 'element' 5 | 6 | module Opal 7 | module RSpec 8 | class HtmlPrinter < ::RSpec::Core::Formatters::HtmlPrinter 9 | def initialize(output) 10 | super 11 | @group_stack = [] 12 | @update_stack = [] 13 | end 14 | 15 | def print_html_start 16 | # Will output the header 17 | super 18 | # Now close out the doc so we can use DOM manipulation for the rest 19 | @output.puts "" 20 | @output.puts "" 21 | @output.puts "" 22 | @output.puts "" 23 | @output.close 24 | # From here, we'll do more direct DOM manipulation 25 | reset_output 26 | @root_node = Element.klass 'results' 27 | end 28 | 29 | def current_node 30 | @group_stack.last ? @group_stack.last : @root_node 31 | end 32 | 33 | def flush_output 34 | node = current_node 35 | new_node = Element.from_string(@output.string) 36 | node.append new_node 37 | reset_output 38 | end 39 | 40 | def reset_output 41 | @output = NoopFlushStringIO.new 42 | end 43 | 44 | def print_example_group_start(group_id, description, number_of_parents) 45 | super 46 | @output.puts '' 47 | parent_node = current_node 48 | new_node = Element.from_string(@output.string) 49 | reset_output 50 | parent_node << new_node 51 | @group_stack << new_node.get_child_by_tag_name('dl') 52 | # We won't have this in the DOM until group ends, so need to queue up yellow/red updates 53 | @update_stack << [] 54 | end 55 | 56 | def print_example_group_end 57 | @group_stack.pop 58 | @update_stack.pop.each(&:call) 59 | end 60 | 61 | def print_example_passed(description, run_time) 62 | super 63 | flush_output 64 | end 65 | 66 | def print_example_failed(pending_fixed, description, run_time, failure_id, exception, extra_content) 67 | super 68 | flush_output 69 | example_we_just_wrote = current_node.get_child_by_tag_name('dd', index=-1) 70 | dump_message = lambda do |*| 71 | puts "Exception for example '#{description}'\n#{exception[:backtrace]}" 72 | false 73 | end 74 | button = Element.from_string('
') 75 | button.on_click = dump_message 76 | example_we_just_wrote << button 77 | end 78 | 79 | def print_example_pending(description, pending_message) 80 | super 81 | flush_output 82 | end 83 | 84 | def print_summary(duration, example_count, failure_count, pending_count) 85 | # string mutation 86 | totals = "#{example_count} example#{'s' unless example_count == 1}, " 87 | totals += "#{failure_count} failure#{'s' unless failure_count == 1}" 88 | totals += ", #{pending_count} pending" if pending_count > 0 89 | 90 | formatted_duration = "%.5f" % duration 91 | Element.id('duration').html = "Finished in #{formatted_duration} seconds" 92 | Element.id('totals').html = totals 93 | end 94 | 95 | # Directly manipulate scripts here 96 | def move_progress(percent_done) 97 | `moveProgressBar(#{percent_done})` 98 | end 99 | 100 | def make_header_red 101 | `makeRed('rspec-header')` 102 | end 103 | 104 | def make_header_yellow 105 | `makeYellow('rspec-header')` 106 | end 107 | 108 | def make_example_group_header_red(group_id) 109 | @update_stack.last << lambda do 110 | `makeRed(#{"div_group_#{group_id}"})` 111 | `makeRed(#{"example_group_#{group_id}"})` 112 | end 113 | end 114 | 115 | def make_example_group_header_yellow(group_id) 116 | @update_stack.last << lambda do 117 | `makeYellow(#{"div_group_#{group_id}"})` 118 | `makeYellow(#{"example_group_#{group_id}"})` 119 | end 120 | end 121 | end 122 | end 123 | end 124 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/formatter/noop_flush_string_io.rb: -------------------------------------------------------------------------------- 1 | require 'stringio' 2 | 3 | module Opal 4 | module RSpec 5 | class NoopFlushStringIO < StringIO 6 | # make printer happy 7 | def flush 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/pre_require_fixes.rb: -------------------------------------------------------------------------------- 1 | require_relative 'fixes/opal' 2 | require_relative 'fixes/rspec/support/ruby_features' 3 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/spec_opts.rb.erb: -------------------------------------------------------------------------------- 1 | <%= ::Opal::RSpec.spec_opts_code ENV['SPEC_OPTS'] %> 2 | -------------------------------------------------------------------------------- /lib-opal/opal/rspec/sprockets_runner.rb.erb: -------------------------------------------------------------------------------- 1 | # await: true 2 | 3 | # This file is used by Opal::Server to basically load all spec files that 4 | # can be found in the spec/ directory. 5 | 6 | require 'opal' 7 | require 'opal-rspec' 8 | 9 | <% 10 | environment.get_opal_spec_requires.each do |s| %> 11 | require <%= s.inspect %> 12 | <% end %> 13 | require 'opal/rspec/spec_opts' 14 | 15 | ::RSpec::Core::Runner.invoke.__await__ 16 | ::Kernel.exit 17 | -------------------------------------------------------------------------------- /lib/opal-rspec.rb: -------------------------------------------------------------------------------- 1 | require 'opal/rspec' 2 | 3 | -------------------------------------------------------------------------------- /lib/opal/rspec.rb: -------------------------------------------------------------------------------- 1 | require 'opal' 2 | require 'opal/rspec/version' 3 | require 'opal/rspec/runner' 4 | require 'opal/rspec/configuration_parser' 5 | 6 | # Just register our opal code path with opal build tools 7 | Opal.append_path File.expand_path('../../../lib-opal', __FILE__) 8 | Opal.append_path File.expand_path('../../../stubs', __FILE__) 9 | 10 | # Catch our git submodule included directories 11 | %w{rspec rspec-core rspec-expectations rspec-mocks rspec-support diff-lcs}.each do |gem_name| 12 | Opal.append_path File.expand_path("../../../#{gem_name}/upstream/lib", __FILE__) 13 | end 14 | 15 | # Since we have better specs than before (and a script to deal with this), ignoring 16 | Opal::Config.dynamic_require_severity = :ignore 17 | 18 | module Opal 19 | module RSpec 20 | autoload :ProjectInitializer, 'opal/rspec/project_initializer' 21 | 22 | def self.convert_spec_opts(opts) 23 | opts ||= ENV['SPEC_OPTS'] || {} 24 | 25 | unless opts.is_a? Hash 26 | opts = Shellwords.split(opts) if opts.is_a? String 27 | opts = Opal::RSpec::Core::Parser.parse(opts || []) 28 | end 29 | 30 | opts 31 | end 32 | 33 | def self.spec_opts_code(spec_opts) 34 | spec_opts = convert_spec_opts(spec_opts) 35 | 36 | code = [] 37 | code << '# await: true' 38 | 39 | # New API - passthru options 40 | spec_opts[:files_or_directories_to_run] ||= [] 41 | 42 | code << "$rspec_opts = #{spec_opts.inspect}" 43 | code << "$0 = 'opal-rspec'" 44 | 45 | code << '::RSpec::Core::Runner.invoke.__await__' 46 | code.join("\n") 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/opal/rspec/cached_environment.rb: -------------------------------------------------------------------------------- 1 | require 'sprockets' 2 | 3 | module Opal 4 | module RSpec 5 | class CachedEnvironment < ::Sprockets::CachedEnvironment 6 | # this class is accessible from the ERB/runner file 7 | 8 | def initialize(env, locator) 9 | super env 10 | @locator = locator 11 | end 12 | 13 | def get_opal_spec_requires 14 | @locator.get_opal_spec_requires.map do |file| 15 | asset = find_asset(file) 16 | unless asset 17 | raise "Unable to find asset for file #{file} within load paths. Check your load path/file specification.\n"+ 18 | "Searched paths:\n- #{paths.join("\n- ")}\n" 19 | 20 | end 21 | logical_path = asset.logical_path 22 | # These will go directly into require '...' statements in Opal, so need to trim extensions 23 | logical_path.sub File.extname(logical_path), '' 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/opal/rspec/configuration_parser.rb: -------------------------------------------------------------------------------- 1 | require 'opal/rspec/util' 2 | require 'optparse' 3 | 4 | module Opal; module RSpec; module Core; end; end; end 5 | # Load necessary files under Opal's namespace, so as not to conflict with RSpec if it's being loaded too. 6 | # Later, we will monkey-patch those methods. 7 | ::Opal::RSpec.load_namespaced __dir__ + "/../../../rspec-core/upstream/lib/rspec/core/invocations.rb", ::Opal 8 | ::Opal::RSpec.load_namespaced __dir__ + "/../../../rspec-core/upstream/lib/rspec/core/option_parser.rb", ::Opal 9 | 10 | class Opal::RSpec::Core::Parser 11 | alias parser_before_opal parser 12 | 13 | def parser(options) 14 | parser_before_opal(options).tap do |parser| 15 | parser.banner = "Usage: opal-rspec [options] [files or directories]\n\n" 16 | 17 | parser.separator '' 18 | parser.separator ' **** Opal specific options ****' 19 | parser.separator '' 20 | 21 | parser.on('-R', '--runner NAME', 'Use a different JS runner (default is nodejs)') do |name| 22 | options[:runner] = name 23 | end 24 | 25 | parser.on('-q', '--rbrequire FILE', 'Require a file in MRI context before running Opal') do |name| 26 | options[:opal_rbrequires] ||= [] 27 | options[:opal_rbrequires] << name 28 | end 29 | 30 | parser.on('-O', '--opal-opt FLAG', 'Run Opal with additional options (separate by `,` or specify multiple times)') do |name| 31 | options[:opal_options] ||= [] 32 | options[:opal_options] += name.split(",") 33 | end 34 | 35 | parser.separator '' 36 | parser.separator ' **** Help ****' 37 | parser.separator '' 38 | end 39 | end 40 | end 41 | 42 | class Opal::RSpec::Core::Invocations::PrintVersion 43 | alias call_before_opal call 44 | 45 | def call(options, err, out) 46 | exitcode = call_before_opal(options, err, out) 47 | out.puts "Opal #{Opal::VERSION}" 48 | out.puts " - opal-rspec #{Opal::RSpec::VERSION}" 49 | exitcode 50 | end 51 | end 52 | 53 | module Opal::RSpec::Support 54 | def self.require_rspec_core(arg) 55 | require "opal/rspec/"+arg 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/opal/rspec/locator.rb: -------------------------------------------------------------------------------- 1 | require 'opal/rspec/util' 2 | require 'pathname' 3 | require 'rake' 4 | # require the bundled RSpec's file and don't rely on the load path in case opal-rspec is included in a project's 5 | # Gemfile without rspec also being in the Gemfile 6 | ::Opal::RSpec.load_namespaced __dir__+'/../../../rspec-core/upstream/lib/rspec/core/ruby_project.rb', ::Opal 7 | 8 | module Opal 9 | module RSpec 10 | class Locator 11 | include ::Opal::RSpec::Core::RubyProject 12 | 13 | DEFAULT_GLOB = '**{,/*/**}/*_spec{.js,}.{rb,opal}' 14 | DEFAULT_DEFAULT_PATH = 'spec-opal' 15 | 16 | attr_accessor :spec_pattern, :spec_exclude_pattern, :spec_files, :default_path 17 | 18 | def initialize(pattern: nil, exclude_pattern: nil, files: nil, default_path: nil) 19 | @spec_exclude_pattern = Array(exclude_pattern) 20 | @spec_files = files 21 | @default_path = default_path || DEFAULT_DEFAULT_PATH 22 | @spec_pattern = Array(pattern || DEFAULT_GLOB) 23 | 24 | @spec_pattern = @spec_pattern.map do |pattern| 25 | pattern.sub(/\A#{Regexp.escape(@default_path)}/, '') 26 | end 27 | 28 | @spec_exclude_pattern = @spec_exclude_pattern.map do |pattern| 29 | pattern.sub(/\A#{Regexp.escape(@default_path)}/, '') 30 | end 31 | end 32 | 33 | def determine_root 34 | find_first_parent_containing(@default_path) || '.' 35 | end 36 | 37 | def get_spec_load_paths 38 | [File.join(root, @default_path)] 39 | end 40 | 41 | def get_matching_files_under(path: ) 42 | FileList[*@spec_pattern.map { |i| "#{path}/#{i}" }] 43 | .exclude(*@spec_exclude_pattern.map { |i| "#{path}/#{i}" }) 44 | end 45 | 46 | def get_opal_spec_requires 47 | if !@spec_files || @spec_files.empty? 48 | files = get_matching_files_under(path: @default_path) 49 | else 50 | files = @spec_files.map do |file| 51 | file = file.split(/[\[:]/).first 52 | if File.directory?(file) 53 | get_matching_files_under(path: file).to_a 54 | else 55 | file 56 | end 57 | end.flatten 58 | files = FileList[*files] 59 | end 60 | files.uniq.map { |file| File.expand_path file } 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/opal/rspec/project_initializer.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | module Opal 4 | module RSpec 5 | # @private 6 | # Generates conventional files for an rspec project 7 | class ProjectInitializer 8 | attr_reader :destination, :stream, :template_path 9 | 10 | DOT_RSPEC_FILE = '.rspec-opal' 11 | SPEC_HELPER_FILE = 'spec-opal/spec_helper.rb' 12 | 13 | def initialize(opts={}) 14 | @destination = opts.fetch(:destination, Dir.getwd) 15 | @stream = opts.fetch(:report_stream, $stdout) 16 | @template_path = opts.fetch(:template_path) do 17 | File.expand_path("../project_initializer", __FILE__) 18 | end 19 | end 20 | 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 33 | copy_template DOT_RSPEC_FILE 34 | copy_template SPEC_HELPER_FILE 35 | end 36 | 37 | private 38 | 39 | def copy_template(file) 40 | destination_file = File.join(destination, file) 41 | return report_exists(file) if File.exist?(destination_file) 42 | 43 | report_creating(file) 44 | FileUtils.mkdir_p(File.dirname(destination_file)) 45 | File.open(destination_file, 'w') do |f| 46 | f.write File.read(File.join(template_path, file)) 47 | end 48 | end 49 | 50 | def report_exists(file) 51 | stream.puts " exist #{file}" 52 | end 53 | 54 | def report_creating(file) 55 | stream.puts " create #{file}" 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/opal/rspec/project_initializer/.rspec-opal: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /lib/opal/rspec/project_initializer/spec-opal/spec_helper.rb: -------------------------------------------------------------------------------- 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 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 | 86 | # Run specs in random order to surface order dependencies. If you find an 87 | # order dependency and want to debug it, you can fix the order by providing 88 | # the seed, which is printed after each run. 89 | # --seed 1234 90 | config.order = :random 91 | 92 | # Seed global randomization in this process using the `--seed` CLI option. 93 | # Setting this allows you to use `--seed` to deterministically reproduce 94 | # test failures related to randomization by passing the same `--seed` value 95 | # as the one that triggered the failure. 96 | Kernel.srand config.seed 97 | =end 98 | end 99 | -------------------------------------------------------------------------------- /lib/opal/rspec/rake_task.rb: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'opal/rspec/runner' 3 | 4 | module Opal 5 | module RSpec 6 | class RakeTask 7 | include Rake::DSL 8 | DEFAULT_NAME = 'spec:opal' 9 | attr_reader :rake_task 10 | 11 | def initialize(name = DEFAULT_NAME, &block) 12 | runner = ::Opal::RSpec::Runner.new(&block) 13 | desc 'Run Opal specs' 14 | @rake_task = task name do 15 | exit runner.run 16 | end 17 | end 18 | end 19 | end 20 | end 21 | 22 | -------------------------------------------------------------------------------- /lib/opal/rspec/sprockets.rb: -------------------------------------------------------------------------------- 1 | require 'opal/rspec' 2 | require 'opal/rspec/sprockets_environment' 3 | 4 | Opal::Config.arity_check_enabled = true 5 | Opal::Config.enable_source_location = true 6 | Opal::Config.enable_file_source_embed = true 7 | -------------------------------------------------------------------------------- /lib/opal/rspec/sprockets_environment.rb: -------------------------------------------------------------------------------- 1 | require 'sprockets' 2 | require 'pathname' 3 | require 'opal/rspec/cached_environment' 4 | require 'opal/rspec/locator' 5 | require 'forwardable' 6 | 7 | module Opal 8 | module RSpec 9 | class SprocketsEnvironment < ::Sprockets::Environment 10 | extend Forwardable 11 | # this class accessible from config.ru and the rask task initializer 12 | 13 | def_delegators :@locator, 14 | :spec_pattern=, 15 | :spec_pattern, 16 | :spec_exclude_pattern=, 17 | :spec_exclude_pattern, 18 | :spec_files=, 19 | :spec_files, 20 | :default_path=, 21 | :default_path 22 | 23 | def initialize(spec_pattern=nil, spec_exclude_pattern=nil, spec_files=nil, default_path=nil) 24 | @locator = Opal::RSpec::Locator.new(pattern: spec_pattern, exclude_pattern: spec_exclude_pattern, files: spec_files, default_path: default_path) 25 | super() 26 | end 27 | 28 | attr_reader :locator 29 | 30 | def add_spec_paths_to_sprockets 31 | locator.get_spec_load_paths.each { |p| append_path p } 32 | end 33 | 34 | def cached 35 | CachedEnvironment.new(self, @locator) 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/opal/rspec/util.rb: -------------------------------------------------------------------------------- 1 | module ::Opal 2 | module RSpec 3 | def self.load_namespaced(file, mod) 4 | if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.1") 5 | load file, mod 6 | else 7 | str = "" 8 | str += "module ::#{mod.name};" 9 | str += File.read(file) 10 | str += ";end" 11 | eval(str) 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/opal/rspec/version.rb: -------------------------------------------------------------------------------- 1 | require 'opal/rspec/util' 2 | 3 | module Opal 4 | module RSpec 5 | VERSION = '1.1.0.alpha3' 6 | end 7 | end 8 | 9 | ::Opal::RSpec.load_namespaced __dir__ + "/../../../rspec-core/upstream/lib/rspec/core/version.rb", ::Opal 10 | ::Opal::RSpec.load_namespaced __dir__ + "/../../../rspec-expectations/upstream/lib/rspec/expectations/version.rb", ::Opal 11 | ::Opal::RSpec.load_namespaced __dir__ + "/../../../rspec-mocks/upstream/lib/rspec/mocks/version.rb", ::Opal 12 | ::Opal::RSpec.load_namespaced __dir__ + "/../../../rspec-support/upstream/lib/rspec/support/version.rb", ::Opal 13 | -------------------------------------------------------------------------------- /opal-rspec.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "opal/rspec/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "opal-rspec" 8 | spec.version = Opal::RSpec::VERSION 9 | spec.authors = ['Adam Beynon', 'Brady Wied', 'Elia Schito'] 10 | spec.email = ['elia@schito.me'] 11 | 12 | spec.summary = %q{RSpec for Opal} 13 | spec.description = %q{Opal compatible RSpec library} 14 | spec.homepage = 'https://github.com/opal/opal-rspec' 15 | spec.license = "MIT" 16 | 17 | spec.files = Dir.chdir(__dir__){ 18 | `git ls-files --recurse-submodules -z` 19 | }.split("\x0").reject do |f| 20 | f.match(%r{^(spec|rspec(-\w+)?(/upstream)?/spec/)}) 21 | end 22 | 23 | spec.bindir = "exe" 24 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 25 | spec.require_paths = ["lib"] 26 | 27 | spec.add_dependency 'opal', ['>= 1.6.0a', '< 2.0'] 28 | spec.add_dependency 'rake', '>= 12.0' 29 | 30 | spec.add_development_dependency 'bundler' 31 | spec.add_development_dependency 'yard' 32 | spec.add_development_dependency 'apparition' 33 | spec.add_development_dependency 'capybara' 34 | spec.add_development_dependency 'launchy' 35 | spec.add_development_dependency 'appraisal' 36 | end 37 | 38 | -------------------------------------------------------------------------------- /rspec-core/spec/files_to_exclude.txt: -------------------------------------------------------------------------------- 1 | #**/core/configuration_spec.rb 2 | **/shared_example_group_spec.rb 3 | **/rspec/core/configuration_options_spec.rb 4 | **/html_formatter_spec.rb 5 | **/snippet_extractor_spec.rb 6 | **/rake_task_spec.rb 7 | **/option_parser_spec.rb 8 | **/runner_spec.rb 9 | **/backtrace_formatter_spec.rb 10 | **/ordering_spec.rb 11 | **/project_initializer_spec.rb 12 | **/bisect_spec.rb 13 | **/bisect/*_spec.rb 14 | -------------------------------------------------------------------------------- /rspec-core/spec/fixes/missing_constants.rb: -------------------------------------------------------------------------------- 1 | def Dir.[](_glob) 2 | [] 3 | end 4 | 5 | def Dir.mktmpdir(*) 6 | '' 7 | end 8 | 9 | module Aruba 10 | module Api 11 | end 12 | end 13 | 14 | # None of this is supported in Opal 15 | module RSpec::Support::ShellOut 16 | end 17 | 18 | module MathnIntegrationSupport 19 | def with_mathn_loaded 20 | yield 21 | end 22 | end 23 | 24 | module Open3 25 | end 26 | 27 | module DRb 28 | class DRbServerNotFound < StandardError; end unless defined? DRbServerNotFound 29 | 30 | def self.current_server 31 | raise DRbServerNotFound 32 | end 33 | 34 | def self.stop_service 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /rspec-core/spec/fixes/shared_examples.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_examples_for "a library that issues no warnings when loaded" do |*| 2 | it('fails') 3 | end 4 | RSpec.shared_examples_for "spec files" do |*| 5 | it('fails') 6 | end 7 | RSpec.shared_examples_for "library wide checks" do |lib, *preamble_stmnts| 8 | it('fails') 9 | end 10 | -------------------------------------------------------------------------------- /rspec-core/spec/requires.rb: -------------------------------------------------------------------------------- 1 | class ::RSpec::Core::Ordering::Random 2 | # there are a lot of these in the RSpec specs that create noise 3 | HIDE_RANDOM_WARNINGS = true 4 | end 5 | 6 | # dealing with dynamic requires 7 | require 'rspec/support' 8 | require 'rspec/support/spec/deprecation_helpers' 9 | require 'rspec/support/spec/with_isolated_stderr' 10 | require 'rspec/support/spec/stderr_splitter' 11 | require 'rspec/support/spec/formatting_support' 12 | require 'rspec/support/spec/with_isolated_directory' 13 | require 'rspec/support/spec/in_sub_process' 14 | require 'rspec/support/ruby_features' 15 | require 'support/shared_example_groups' 16 | require 'support/helper_methods' 17 | require 'support/matchers' 18 | require 'support/formatter_support' 19 | require 'support/config_options_helper' 20 | require 'fixes/missing_constants' 21 | require 'fixes/shared_examples' 22 | require 'rspec/support/spec' 23 | require 'opal/fixes/deprecation_helpers' 24 | require 'opal/fixes/rspec_helpers' 25 | require 'filters' 26 | 27 | module StubWriteFile 28 | def write_file(filename, content) 29 | # noop 30 | end 31 | end 32 | 33 | RSpec.configure do |config| 34 | #c.full_description = 'uses the default color for the shared example backtrace line' 35 | config.add_formatter RSpec::Core::Formatters::JsonFormatter, File.open('/tmp/rspec-core-results.json', 'w') 36 | config.add_formatter RSpec::Core::Formatters::ProgressFormatter, $stdout 37 | config.include StubWriteFile 38 | config.filter_run_excluding type: :drb 39 | config.filter_run_excluding isolated_directory: true 40 | end 41 | -------------------------------------------------------------------------------- /rspec-expectations/spec/files_to_exclude.txt: -------------------------------------------------------------------------------- 1 | # opal-minispec integration not working yet 2 | **/minitest_integration_spec.rb 3 | **/throw_symbol_spec.rb 4 | -------------------------------------------------------------------------------- /rspec-expectations/spec/fixes/missing_constants.rb: -------------------------------------------------------------------------------- 1 | # None of this is supported in Opal 2 | module RSpec::Support::ShellOut 3 | end 4 | 5 | # Uses forking/threads 6 | module ::RSpec::Support::InSubProcess 7 | end 8 | 9 | module MinitestIntegration 10 | end 11 | -------------------------------------------------------------------------------- /rspec-expectations/spec/fixes/shared_examples.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_examples_for "a library that issues no warnings when loaded" do |lib, *preamble_stmnts| 2 | it('fails') 3 | end 4 | 5 | RSpec.shared_examples_for "an RSpec value matcher" do |*args| 6 | it('fails') 7 | end 8 | 9 | RSpec.shared_examples_for "an RSpec block-only matcher" do |*args| 10 | it('fails') 11 | end 12 | 13 | RSpec.shared_examples_for "library wide checks" do |lib, *preamble_stmnts| 14 | it('fails') 15 | end 16 | -------------------------------------------------------------------------------- /rspec-expectations/spec/requires.rb: -------------------------------------------------------------------------------- 1 | class ::RSpec::Core::Ordering::Random 2 | # there are a lot of these in the RSpec specs that create noise 3 | HIDE_RANDOM_WARNINGS = true 4 | end 5 | 6 | class Proc 7 | def source_location 8 | ['(dummy)', 0] 9 | end 10 | end 11 | 12 | require 'corelib/marshal' 13 | require 'rspec/core' 14 | require "rspec/support/spec/deprecation_helpers" 15 | require "rspec/support/spec/with_isolated_stderr" 16 | require "rspec/support/spec/stderr_splitter" 17 | require "rspec/support/spec/formatting_support" 18 | require "rspec/support/spec/with_isolated_directory" 19 | require "rspec/support/ruby_features" 20 | require 'rspec/support/spec' 21 | require 'rspec/core/formatters/helpers' 22 | require 'fixes/shared_examples' 23 | require 'support/matchers' 24 | require 'spec_helper' 25 | require 'filters' 26 | 27 | RSpec.configure do |c| 28 | #c.full_description = 'uses the default color for the shared example backtrace line' 29 | c.add_formatter RSpec::Core::Formatters::JsonFormatter, File.open('/tmp/rspec-expectations-results.json', 'w') 30 | c.add_formatter RSpec::Core::Formatters::ProgressFormatter, $stdout 31 | end 32 | -------------------------------------------------------------------------------- /rspec-mocks/spec/files_to_exclude.txt: -------------------------------------------------------------------------------- 1 | # Do not support marshalling in Opal 2 | **/marshal_extension_spec.rb 3 | 4 | # No private, public, etc. 5 | **/method_visibility_spec.rb 6 | 7 | # class_double depends on ClassVerifyingDouble inheriting from Module to support transferring nested constants, but that doesn't work on Opal 8 | **/class_double*_spec.rb 9 | 10 | # YAML/marshal serialization 11 | **/serialization_spec.rb 12 | 13 | # Dropping errors 14 | **/verifying_doubles/naming_spec.rb 15 | **/mocks_spec.rb 16 | -------------------------------------------------------------------------------- /rspec-mocks/spec/fixes/no_const_hide.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Mocks 3 | # Contains methods intended to be used from within code examples. 4 | # Mix this in to your test context (such as a test framework base class) 5 | # to use rspec-mocks with your test framework. If you're using rspec-core, 6 | # it'll take care of doing this for you. 7 | module ExampleMethods 8 | # Don't hide the Float... Opal breaks then 9 | def hide_const(constant_name) 10 | ConstantMutator.hide(constant_name) unless constant_name == "Float" 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /rspec-mocks/spec/fixes/shared_examples.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_examples_for "a library that issues no warnings when loaded" do |*| 2 | it('fails') 3 | end 4 | RSpec.shared_examples_for "spec files" do |*| 5 | it('fails') 6 | end 7 | RSpec.shared_examples_for "library wide checks" do |lib, *preamble_stmnts| 8 | it('fails') 9 | end 10 | -------------------------------------------------------------------------------- /rspec-mocks/spec/requires.rb: -------------------------------------------------------------------------------- 1 | class ::RSpec::Core::Ordering::Random 2 | # there are a lot of these in the RSpec specs that create noise 3 | HIDE_RANDOM_WARNINGS = true 4 | end 5 | 6 | # dealing with dynamic requires 7 | require 'spec_helper' 8 | require 'rspec/support/spec/deprecation_helpers' 9 | require 'rspec/support/spec/with_isolated_stderr' 10 | require 'rspec/support/spec/formatting_support' 11 | require 'rspec/support/spec/with_isolated_directory' 12 | require 'opal-parser' 13 | require 'fixes/shared_examples' 14 | require 'fixes/no_const_hide' 15 | require 'corelib/marshal' 16 | require 'filters' 17 | 18 | RSpec.configure do |c| 19 | #c.full_description = 'uses the default color for the shared example backtrace line' 20 | c.add_formatter RSpec::Core::Formatters::JsonFormatter, File.open('/tmp/rspec-mocks-results.json', 'w') 21 | c.add_formatter RSpec::Core::Formatters::ProgressFormatter, $stdout 22 | end 23 | 24 | -------------------------------------------------------------------------------- /rspec-support/spec/files_to_exclude.txt: -------------------------------------------------------------------------------- 1 | # InSubprocess / forking 2 | **/in_sub_process_spec.rb 3 | 4 | # No diff support yet on Opal 5 | **/differ_spec.rb 6 | 7 | # File I/O 8 | **/directory_maker_spec.rb 9 | 10 | # Shell not available on Opal 11 | **/shell_out_spec.rb 12 | 13 | # File I/O 14 | **/caller_filter_spec.rb 15 | 16 | # Uses eval 17 | **/method_signature_verifier_spec.rb 18 | 19 | # Not supported on Opal 20 | **/encoded_string_spec.rb 21 | **/reentrant_mutex_spec.rb 22 | -------------------------------------------------------------------------------- /rspec-support/spec/filters.rb: -------------------------------------------------------------------------------- 1 | require 'opal_filters' 2 | 3 | OpalFilters.group('Bugs') do 4 | fails "RSpec::Support::ObjectFormatter with a hash object sorts keys to ensure objects are always displayed the same way", "expected: \"{:a=>\\\"aaa\\\", \\\"b\\\"=>\\\"bbb\\\", :c=>\\\"ccc\\\"}\"" 5 | fails "RSpec::Support::ObjectFormatter with Time objects produces an extended output", "expected \"1969-12-31 19:01:40.000000 -0000\" to include \"1969-12-31 19:01:40.000101\"" 6 | fails "RSpec::Support::ObjectFormatter with DateTime objects when ActiveSupport is loaded uses a custom format to ensure the output is different when DateTimes differ", "expected: \"Sat, 01 Jan 2000 01:01:00.100000000 +0000\"" 7 | fails "RSpec::Support::ObjectFormatter with BigDecimal objects uses Ruby's BigDecimal formatting since it is improved in 2.4+", "expected: \"0.33e1\"" 8 | fails "RSpec::Support::ObjectFormatter given a delegator includes the delegator class in the description", "undefined method `__getobj__' for #" 9 | fails "RSpec::Support::ObjectFormatter given a delegator includes the delegator class in the description even when protected", "undefined method `__getobj__' for #" 10 | fails "RSpec::Support::ObjectFormatter given a delegator for a specially-formatted object formats the underlying object normally", "undefined method `__getobj__' for 3.3" 11 | fails "RSpec::Support::ObjectFormatter with a non-immediate recursive array formats the recursive element as [...]", "expected: \"[{:recursive_array=>[...]}]\"" 12 | fails "RSpec::Support::ObjectFormatter with a non-immediate recursive hash formats the recursive element as {...}", "expected: \"{:array=>[:next_is_recursive_hash, {...}]}\"" 13 | fails "RSpec::Support::ObjectFormatter with an array including a same collection object multiple times does not omit them", "expected: \"[{:key=>\\\"value\\\"}, {:key=>\\\"value\\\"}]\"" 14 | fails "RSpec::Support::ObjectFormatter with truncation enabled with ANSI escape codes that fall on the truncate split removes that escape code so terminals do not get corrupted print a partial escape code", "expected: \"#<\\e[33mClass\\e[0m ...\\e[36mcount: \\e[0m42>\"" 15 | fails "RSpec::Support::StdErrSplitter supports methods that stderr supports but StringIO does not", "undefined method `stat' for # RUBY_VERSION == '1.9.2' do 19 | groups = 10.times.map { ExampleGroup.describe("Collision") } 20 | expect(groups[0]).to have_class_const("Collision") 21 | expect(groups[1]).to have_class_const("Collision_2") 22 | expect(groups[8]).to have_class_const("Collision_9") 23 | 24 | if RUBY_VERSION.to_f > 1.8 && !(defined?(RUBY_ENGINE) && ['rbx', 'opal'].include?(RUBY_ENGINE)) 25 | # on 1.8.7, rbx "Collision_9".next => "Collisioo_0" 26 | expect(groups[9]).to have_class_const("Collision_10") 27 | end 28 | end 29 | end 30 | 31 | describe "#before, after, and around hooks" do 32 | it "treats an error in before(:each) as a failure" do 33 | group = ExampleGroup.describe 34 | group.before(:each) { raise "error in before each" } 35 | example = group.example("equality") { expect(1).to eq(2) } 36 | #expect(group.run).to be(false) 37 | expect(group.run).to be_a PromiseV2 38 | expect(group.run_await).to be_falsey 39 | 40 | expect(example.execution_result.exception.message).to eq("error in before each") 41 | end 42 | 43 | it "treats an error in before(:all) as a failure" do 44 | group = ExampleGroup.describe 45 | group.before(:all) { raise "error in before all" } 46 | example = group.example("equality") { expect(1).to eq(2) } 47 | #expect(group.run).to be_falsey 48 | expect(group.run).to be_a PromiseV2 49 | expect(group.run_await).to be_falsey 50 | 51 | expect(example.metadata).not_to be_nil 52 | expect(example.execution_result.exception).not_to be_nil 53 | expect(example.execution_result.exception.message).to eq("error in before all") 54 | end 55 | end 56 | 57 | describe "#run_examples" do 58 | let(:reporter) { double("reporter").as_null_object } 59 | 60 | it "returns false if any of the examples fail" do 61 | group = ExampleGroup.describe('group') do 62 | example('ex 1') { expect(1).to eq(1) } 63 | example('ex 2') { expect(1).to eq(2) } 64 | end 65 | allow(group).to receive(:filtered_examples) { group.examples } 66 | # expect(group.run(reporter)).to be_falsey 67 | # Promise 68 | result = group.run(reporter) 69 | expect(group.run(reporter).value).to be_falsey 70 | end 71 | 72 | it "runs all examples, regardless of any of them failing" do 73 | group = ExampleGroup.describe('group') do 74 | example('ex 1') { expect(1).to eq(2) } 75 | example('ex 2') { expect(1).to eq(1) } 76 | end 77 | allow(group).to receive(:filtered_examples) { group.examples } 78 | group.filtered_examples.each do |example| 79 | expect(example).to receive(:run) 80 | end 81 | # expect(group.run(reporter)).to be_falsey 82 | # Promise 83 | expect(group.run(reporter).value).to be_falsey 84 | end 85 | end 86 | 87 | describe "#run" do 88 | let(:reporter) { double("reporter").as_null_object } 89 | 90 | context "with fail_fast? => true" do 91 | let(:group) do 92 | group = RSpec::Core::ExampleGroup.describe 93 | allow(group).to receive(:fail_fast?) { true } 94 | group 95 | end 96 | 97 | it "sets RSpec.world.wants_to_quit flag if encountering an exception in before(:all)" do 98 | group.before(:all) { raise "error in before all" } 99 | group.example("equality") { expect(1).to eq(2) } 100 | # This method returns a promise in Opal 101 | #expect(group.run).to be_falsey 102 | expect(group.run.value).to be_falsey 103 | expect(RSpec.world.wants_to_quit).to be_truthy 104 | end 105 | end 106 | 107 | context "with top level example failing" do 108 | it "returns false" do 109 | group = RSpec::Core::ExampleGroup.describe("something") do 110 | it "does something (wrong - fail)" do 111 | raise "fail" 112 | end 113 | describe "nested" do 114 | it "does something else" do 115 | # pass 116 | end 117 | end 118 | end 119 | 120 | #expect(group.run(reporter)).to be_falsey 121 | # Promise 122 | expect(group.run(reporter).value).to be_falsey 123 | end 124 | end 125 | 126 | context "with nested example failing" do 127 | it "returns true" do 128 | group = RSpec::Core::ExampleGroup.describe("something") do 129 | it "does something" do 130 | # pass 131 | end 132 | describe "nested" do 133 | it "does something else (wrong -fail)" do 134 | raise "fail" 135 | end 136 | end 137 | end 138 | 139 | #expect(group.run(reporter)).to be_falsey 140 | # Promise 141 | expect(group.run(reporter).value).to be_falsey 142 | end 143 | end 144 | end 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /spec-opal-rspec/core/failed_example_notification_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | module RSpec::Core::Notifications 4 | describe 'Opal FailedExampleNotification' do 5 | before do 6 | allow(RSpec.configuration).to receive(:color_enabled?).and_return(true) 7 | end 8 | 9 | it "uses the default color for the shared example backtrace line" do 10 | example = nil 11 | group = RSpec::Core::ExampleGroup.describe "testing" do 12 | shared_examples_for "a" do 13 | example = it "fails" do 14 | expect(1).to eq(2) 15 | end 16 | end 17 | it_behaves_like "a" 18 | end 19 | group.run 20 | fne = FailedExampleNotification.new(example) 21 | lines = fne.colorized_message_lines 22 | #expect(lines).to include(match("\\e\\[37mShared Example Group:")) 23 | # Javascript console code 24 | matcher = /.*Shared Example Group.*/ 25 | line = lines.find { |l| matcher.match l } 26 | escape = "\033" 27 | # Have to string concat this for it to work properly 28 | expect(line).to eq(escape +"[31m" + escape+"[37mShared Example Group: \"a\" called from "+escape+"[0m"+escape+"[0m") 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec-opal-rspec/core/hooks_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | module RSpec::Core 4 | RSpec.describe 'Opal RSpec::Core::Hooks' do 5 | context "when an error happens in `after(:suite)`" do 6 | it 'allows the error to propagate to the user' do 7 | RSpec.configuration.after(:suite) { 1 / 0 } 8 | 9 | # expect { 10 | # RSpec.configuration.hooks.run(:after, :suite, SuiteHookContext.new) 11 | # }.to raise_error(ZeroDivisionError) 12 | # hooks returns a promise, have to wait for that 13 | RSpec.configuration.hooks.run(:after, :suite, SuiteHookContext.new).then do 14 | raise 'Expected ZeroDivisionError but got none' 15 | end.rescue do |ex| 16 | expect(ex).to be_a? ZeroDivisionError 17 | end 18 | end 19 | end 20 | 21 | context "when an error happens in `before(:suite)`" do 22 | it 'allows the error to propagate to the user' do 23 | RSpec.configuration.before(:suite) { 1 / 0 } 24 | 25 | # expect { 26 | # RSpec.configuration.hooks.run(:before, :suite, SuiteHookContext.new) 27 | # }.to raise_error(ZeroDivisionError) 28 | # hooks return a promise 29 | RSpec.configuration.hooks.run(:before, :suite, SuiteHookContext.new).then do 30 | raise 'Expected ZeroDivisionError but got none' 31 | end.rescue do |ex| 32 | expect(ex).to be_a? ZeroDivisionError 33 | end 34 | end 35 | end 36 | 37 | describe '#around' do 38 | context 'when it does not run the example' do 39 | context 'for a hook declared in the group' do 40 | it 'converts the example to a skipped example so the user is made aware of it' do 41 | ex = nil 42 | group = RSpec.describe do 43 | around {} 44 | ex = example("not run") {} 45 | end 46 | 47 | # promise 48 | # group.run 49 | # expect(ex.execution_result.status).to eq(:pending) 50 | group.run.then do 51 | expect(ex.execution_result.status).to eq(:pending) 52 | end 53 | end 54 | end 55 | 56 | context 'for a hook declared in config' do 57 | it 'converts the example to a skipped example so the user is made aware of it' do 58 | RSpec.configuration.around {} 59 | 60 | ex = nil 61 | group = RSpec.describe do 62 | ex = example("not run") {} 63 | end 64 | 65 | # promise 66 | # group.run 67 | # expect(ex.execution_result.status).to eq(:pending) 68 | group.run.then do 69 | expect(ex.execution_result.status).to eq(:pending) 70 | end 71 | end 72 | end 73 | end 74 | 75 | it 'considers the hook to have run when passed as a block to a method that yields' do 76 | ex = nil 77 | group = RSpec.describe do 78 | def transactionally 79 | yield 80 | end 81 | 82 | around { |e| transactionally(&e) } 83 | ex = example("run") {} 84 | end 85 | 86 | # promise 87 | # group.run 88 | # expect(ex.execution_result.status).to eq(:passed) 89 | group.run.then do 90 | expect(ex.execution_result.status).to eq(:passed) 91 | end 92 | end 93 | 94 | it 'does not consider the hook to have run when passed as a block to a method that does not yield' do 95 | ex = nil 96 | group = RSpec.describe do 97 | def transactionally; 98 | end 99 | 100 | around { |e| transactionally(&e) } 101 | ex = example("not run") {} 102 | end 103 | 104 | # promise 105 | # group.run 106 | # expect(ex.execution_result.status).to eq(:pending) 107 | group.run.then do 108 | expect(ex.execution_result.status).to eq(:pending) 109 | end 110 | end 111 | end 112 | end 113 | end 114 | -------------------------------------------------------------------------------- /spec-opal-rspec/core/memoized_helpers_spec.rb: -------------------------------------------------------------------------------- 1 | # await: *await* 2 | 3 | require 'spec_helper' 4 | 5 | module RSpec::Core 6 | RSpec.describe 'Opal MemoizedHelpers' do 7 | before(:each) { RSpec.configuration.configure_expectation_framework } 8 | 9 | context "using 'self' as an explicit subject" do 10 | it "delegates matcher to the ExampleGroup" do 11 | group = ExampleGroup.describe("group") do 12 | subject { self } 13 | def ok?; true; end 14 | def not_ok?; false; end 15 | 16 | it { is_expected.to eq(self) } 17 | it { is_expected.to be_ok } 18 | it { is_expected.to_not be_not_ok } 19 | end 20 | 21 | #expect(group.run).to be true 22 | expect(group.run).to be_a PromiseV2 23 | expect(group.run_await).to be_truthy 24 | end 25 | 26 | it 'supports a new expect-based syntax' do 27 | group = ExampleGroup.describe([1, 2, 3]) do 28 | it { is_expected.to be_an Array } 29 | it { is_expected.not_to include 4 } 30 | end 31 | 32 | #expect(group.run).to be true 33 | expect(group.run).to be_a PromiseV2 34 | expect(group.run_await).to be_truthy 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec-opal-rspec/core/metadata_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module RSpec::Core 4 | RSpec.describe 'Opal Metadata' do 5 | describe "backwards compatibility" do 6 | before { allow_deprecation } 7 | 8 | describe ":example_group" do 9 | it 'allows integration libraries like VCR to infer a fixture name from the example description by walking up nesting structure' do 10 | fixture_name_for = lambda do |metadata| 11 | description = metadata[:description] 12 | 13 | if example_group = metadata[:example_group] 14 | [fixture_name_for[example_group], description].join('/') 15 | else 16 | description 17 | end 18 | end 19 | 20 | ex = inferred_fixture_name = nil 21 | 22 | RSpec.configure do |config| 23 | config.before(:example, :infer_fixture) { |e| inferred_fixture_name = fixture_name_for[e.metadata] } 24 | end 25 | 26 | # run returns a promise 27 | # RSpec.describe "Group", :infer_fixture do 28 | # ex = example("ex") {} 29 | # end.run 30 | # 31 | # raise ex.execution_result.exception if ex.execution_result.exception 32 | # 33 | # expect(inferred_fixture_name).to eq("Group/ex") 34 | 35 | group = RSpec.describe "Group", :infer_fixture do 36 | ex = example("ex") {} 37 | end 38 | 39 | group.run.then do 40 | raise ex.execution_result.exception if ex.execution_result.exception 41 | 42 | expect(inferred_fixture_name).to eq("Group/ex") 43 | end 44 | end 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec-opal-rspec/expectations/be_instance_of_spec.rb: -------------------------------------------------------------------------------- 1 | [:be_an_instance_of, :be_instance_of].each do |method| 2 | describe "Opal expect(actual).to #{method}(expected)" do 3 | it "provides a description" do 4 | matcher = be_an_instance_of(Fixnum) 5 | matcher.matches?(Numeric) 6 | # opal fixnum == numeric 7 | # expect(matcher.description).to eq "be an instance of Fixnum" 8 | expect(matcher.description).to eq "be an instance of Numeric" 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec-opal-rspec/expectations/dsl_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'Opal RSpec::Matchers::DSL::Matcher' do 2 | context "defined using the dsl" do 3 | it "raises NoMethodError for methods not in the running_example" do |example| 4 | RSpec::Matchers.define(:__raise_no_method_error) do 5 | match do |actual| 6 | self.a_method_not_in_the_example == "method defined in the example" 7 | end 8 | end 9 | 10 | # mutable strings 11 | #expected_msg = "RSpec::Matchers::DSL::Matcher" 12 | #expected_msg << " __raise_no_method_error" unless rbx? 13 | expected_msg = "RSpec::Matchers::DSL::Matcher" + " __raise_no_method_error" 14 | 15 | expect { 16 | expect(example).to __raise_no_method_error 17 | }.to raise_error(NoMethodError, /#{expected_msg}/) 18 | end 19 | end 20 | 21 | context "wrapping another expectation (expect(...).to eq ...)" do 22 | it "can use the `include` matcher from a `match` block" do 23 | RSpec::Matchers.define(:descend_from) do |mod| 24 | match do |klass| 25 | expect(klass.ancestors).to include(mod) 26 | end 27 | end 28 | 29 | expect(Fixnum).to descend_from(Object) 30 | expect(Fixnum).not_to descend_from(Array) 31 | 32 | expect { 33 | expect(Fixnum).to descend_from(Array) 34 | }.to fail_with(/expected Number to descend from Array/) 35 | # Fixnum = Numeric on Opal 36 | #}.to fail_with(/expected Fixnum to descend from Array/) 37 | 38 | expect { 39 | expect(Fixnum).not_to descend_from(Object) 40 | # Fixnum = Numeric on Opal 41 | }.to fail_with(/expected Number not to descend from Object/) 42 | #}.to fail_with(/expected Fixnum not to descend from Object/) 43 | end 44 | 45 | it "can use the `match` matcher from a `match` block" do 46 | RSpec::Matchers.define(:be_a_phone_number_string) do 47 | match do |string| 48 | # \A and \Z in JS regex 49 | # expect(string).to match(/\A\d{3}\-\d{3}\-\d{4}\z/) 50 | expect(string).to match(/^\d{3}\-\d{3}\-\d{4}$/) 51 | end 52 | end 53 | 54 | expect("206-123-1234").to be_a_phone_number_string 55 | expect("foo").not_to be_a_phone_number_string 56 | 57 | expect { 58 | expect("foo").to be_a_phone_number_string 59 | }.to fail_with(/expected "foo" to be a phone number string/) 60 | 61 | expect { 62 | expect("206-123-1234").not_to be_a_phone_number_string 63 | }.to fail_with(/expected "206-123-1234" not to be a phone number string/) 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /spec-opal-rspec/expectations/expectation_target_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'Opal ExpectationTarget' do 2 | context 'when constructed via #expect' do 3 | it 'fails an invalid negative expectation' do 4 | # Fixnum = Numeric on Opal 5 | # message = /expected 5 not to be a kind of Fixnum/ 6 | message = /expected 5 not to be a kind of Num.*/ 7 | expect { 8 | expect(5).not_to be_a(Fixnum) 9 | }.to fail_with(message) 10 | end 11 | 12 | it 'fails an invalid negative expectation with a split infinitive' do 13 | # Fixnum = Numeric on Opal 14 | # message = /expected 5 not to be a kind of Fixnum/ 15 | message = /expected 5 not to be a kind of Num.*/ 16 | expect { 17 | expect(5).to_not be_a(Fixnum) 18 | }.to fail_with(message) 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec-opal-rspec/expectations/yield_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'Opal yield_successive_args matcher' do 2 | include YieldHelpers 3 | extend YieldHelpers 4 | 5 | it 'has a description' do 6 | expect(yield_successive_args(1, 3).description).to eq("yield successive args(1, 3)") 7 | # symbols == string in opal 8 | # expect(yield_successive_args([:a, 1], [:b, 2]).description).to eq("yield successive args([:a, 1], [:b, 2])") 9 | expect(yield_successive_args([:a, 1], [:b, 2]).description).to eq('yield successive args(["a", 1], ["b", 2])') 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec-opal-rspec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.color = true 3 | end 4 | -------------------------------------------------------------------------------- /spec-opal/after_hooks_spec.rb: -------------------------------------------------------------------------------- 1 | # await: *await* 2 | 3 | require 'spec_helper' 4 | 5 | describe 'hooks' do 6 | describe 'after' do 7 | before :all do 8 | $AHtotal = 0 9 | $AHexample_still_in_progress = nil 10 | end 11 | 12 | after :all do 13 | expected = 13 14 | unless $AHtotal == expected 15 | msg = "Expected #{expected} after hits but got #{$AHtotal}" 16 | `console.error(#{msg})` 17 | end 18 | end 19 | 20 | let(:raise_before_error) { false } 21 | 22 | before do |example| 23 | if raise_before_error 24 | $AHexample_still_in_progress = nil 25 | raise 'before problem' 26 | end 27 | if $AHexample_still_in_progress 28 | raise "Another spec (#{$AHexample_still_in_progress}) is still running, after block problem" 29 | $AHexample_still_in_progress = nil 30 | end 31 | $AHexample_still_in_progress = example.description 32 | end 33 | 34 | let(:raise_after_error) { false } 35 | 36 | context 'sync' do 37 | after do 38 | $AHtotal += 1 39 | $AHexample_still_in_progress = nil 40 | raise 'expected after problem' if raise_after_error 41 | end 42 | 43 | subject { 42 } 44 | 45 | context 'before fails' do 46 | let(:raise_before_error) { true } 47 | 48 | it 'should not reach the example' do 49 | fail 'we reached the example and we should not have!' 50 | end 51 | end 52 | 53 | context 'match succeeds' do 54 | context 'sync match' do 55 | it { is_expected.to eq 42 } 56 | end 57 | 58 | it 'async match' do 59 | delay_with_promise 0 do 60 | expect(subject).to eq 42 61 | end 62 | end 63 | end 64 | 65 | context 'match fails' do 66 | context 'sync match' do 67 | it { is_expected.to eq 43 } 68 | end 69 | 70 | it 'async match' do 71 | delay_with_promise 0 do 72 | expect(subject).to eq 43 73 | end 74 | end 75 | end 76 | 77 | context 'after fails' do 78 | let(:raise_after_error) { true } 79 | 80 | it { is_expected.to eq 42 } 81 | end 82 | 83 | context 'context' do 84 | after :context do 85 | raise 'it failed in the after context!' 86 | end 87 | 88 | it { is_expected.to eq 42 } 89 | end 90 | end 91 | 92 | context 'async' do 93 | after do 94 | delay_with_promise 0 do 95 | $AHtotal += 1 96 | $AHexample_still_in_progress = nil 97 | raise 'after problem' if raise_after_error 98 | end 99 | end 100 | 101 | subject do 102 | delay_with_promise 0 do 103 | 42 104 | end 105 | end 106 | 107 | context 'before(:each) fails properly' do 108 | let(:raise_before_error) { true } 109 | 110 | it { expect(subject.await).to eq 42 } 111 | end 112 | 113 | context 'match succeeds' do 114 | context 'sync match' do 115 | it { expect(subject.await).to eq 42 } 116 | end 117 | 118 | it 'async match' do 119 | delay_with_promise 0 do 120 | expect(subject.await).to eq 42 121 | end 122 | end 123 | end 124 | 125 | context 'match fails properly' do 126 | context 'sync match' do 127 | it { expect(subject.await).to eq 43 } 128 | end 129 | 130 | it 'async match' do 131 | delay_with_promise 0 do 132 | expect(subject.await).to eq 43 133 | end 134 | end 135 | end 136 | 137 | context 'after(:each) fails properly' do 138 | let(:raise_after_error) { true } 139 | 140 | it { expect(subject.await).to eq 42 } 141 | end 142 | end 143 | end 144 | end 145 | -------------------------------------------------------------------------------- /spec-opal/around_hooks_spec.rb: -------------------------------------------------------------------------------- 1 | # await: *await* 2 | 3 | require 'spec_helper' 4 | 5 | describe 'hooks' do 6 | describe 'around' do 7 | RSpec.shared_context :around_count do 8 | before do 9 | @test_in_progress = nil 10 | end 11 | 12 | before :all do 13 | $around_stack = [] 14 | $around_completed = 0 15 | $around_failures = [] 16 | end 17 | 18 | after :all do 19 | raise $around_failures.join "\n" if $around_failures.any? 20 | raise "hooks not empty! #{$around_stack.inspect}" unless $around_stack.empty? 21 | unless $around_completed == $expected_around_hits 22 | msg = "Expected #{$expected_around_hits} around hits but got #{$around_completed} for #{self}" 23 | `console.error(#{msg})` 24 | end 25 | end 26 | end 27 | 28 | let(:fail_before_example_run) { false } 29 | let(:fail_after_example_run) { false } 30 | let(:skip_run) { false } 31 | 32 | context 'sync' do 33 | subject { 42 } 34 | 35 | around do |example| 36 | raise 'around failed before example properly' if fail_before_example_run 37 | look_for = example.description 38 | $around_stack << look_for 39 | example.run_await unless skip_run 40 | last = $around_stack.pop 41 | $around_failures << "Around hook kept executing even though test #{@test_in_progress} was running!" if @test_in_progress 42 | $around_failures << "Around hooks are messed up because we expected #{look_for} but we popped off #{last}" unless last == look_for 43 | $around_completed += 1 44 | raise 'around failed after example properly' if fail_after_example_run 45 | end 46 | 47 | context 'succeeds' do 48 | before :context do 49 | $expected_around_hits = 1 50 | end 51 | include_context :around_count 52 | 53 | it { is_expected.to equal 42 } 54 | end 55 | 56 | context 'fails before example' do 57 | before :context do 58 | $expected_around_hits = 0 59 | end 60 | include_context :around_count 61 | 62 | let(:fail_before_example_run) { true } 63 | 64 | it { is_expected.to equal 42 } 65 | end 66 | 67 | context 'fails after example' do 68 | before :context do 69 | $expected_around_hits = 1 70 | end 71 | include_context :around_count 72 | 73 | let(:fail_after_example_run) { true } 74 | 75 | it { is_expected.to equal 42 } 76 | end 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /spec-opal/async_spec.rb: -------------------------------------------------------------------------------- 1 | require 'opal/rspec/async' 2 | require 'spec_helper' 3 | 4 | describe 'promise' do 5 | let(:foo) { 100 } 6 | 7 | it 'matcher fails properly' do 8 | delay_with_promise 0 do 9 | 1.should == 2 10 | end 11 | end 12 | 13 | it 'matcher succeeds' do 14 | delay_with_promise 0 do 15 | 1.should == 1 16 | end 17 | end 18 | 19 | context 'non-assertion failure in promise' do 20 | it 'no args' do 21 | promise = PromiseV2.new 22 | delay 0 do 23 | promise.reject 24 | end 25 | promise 26 | end 27 | 28 | # We deliberately remove this feature, as this makes RSpec go bonkers... 29 | xit 'string arg' do 30 | promise = PromiseV2.new 31 | delay 0 do 32 | promise.reject 'string failure reason here' 33 | end 34 | promise 35 | end 36 | 37 | it 'exception arg' do 38 | delay_with_promise 0 do 39 | raise TypeError, 'typeerror driven failure reason here' 40 | end 41 | end 42 | end 43 | 44 | context 'skipped' do 45 | it 'via variable', skip: true do 46 | obj = [1, 2, 3, 4] 47 | 48 | delay_with_promise 0 do 49 | obj.should == [2, 2, 3, 4] 50 | end 51 | end 52 | 53 | xit 'via xit' do 54 | obj = [1, 2, 3, 4] 55 | 56 | delay_with_promise 0 do 57 | obj.should == [2, 2, 3, 4] 58 | end 59 | end 60 | 61 | it 'in example, inside promise' do 62 | delay_with_promise 0 do 63 | skip 'want to skip within' 64 | end 65 | end 66 | 67 | it 'in example, outside promise' do 68 | skip 'want to skip within' 69 | delay_with_promise 0 do 70 | 1.should == 1 71 | end 72 | end 73 | end 74 | 75 | context 'pending' do 76 | it 'in example' do 77 | obj = [1, 2, 3, 4] 78 | 79 | delay_with_promise 0 do 80 | pending 'want to pend within' 81 | obj.should == [2, 2, 3, 4] 82 | end 83 | end 84 | 85 | it 'via variable', pending: 'the reason' do 86 | obj = [1, 2, 3, 4] 87 | 88 | delay_with_promise 0 do 89 | obj.should == [2, 2, 3, 4] 90 | end 91 | end 92 | end 93 | 94 | it "should make example fail properly before async block reached" do 95 | expect(:foo).to eq(:baz) 96 | 97 | delay_with_promise(0) do 98 | expect(nil).to eq 'we reached this assertion and we should not have' 99 | end 100 | end 101 | end 102 | 103 | describe 'async/sync mix' do 104 | it 'fails properly if a sync test is among async tests' do 105 | 1.should == 2 106 | end 107 | 108 | it 'is an async test between 2 sync tests' do 109 | delay_with_promise 0 do 110 | 1.should == 1 111 | end 112 | end 113 | 114 | it 'passes correctly if a sync test is among async tests' do 115 | 1.should == 1 116 | end 117 | 118 | it "can finish running after a long delay and fail properly" do 119 | @test_in_progress = 'can finish running after a long delay and fail' 120 | obj = [1, 2, 3, 4] 121 | 122 | delay_with_promise 1 do 123 | obj.should == [2, 2, 3, 4] 124 | @test_in_progress = nil 125 | end 126 | end 127 | 128 | it "can finish running after a long delay and succeed" do 129 | obj = [1, 2, 3, 4] 130 | 131 | delay_with_promise 1 do 132 | obj.should == [1, 2, 3, 4] 133 | end 134 | end 135 | end 136 | -------------------------------------------------------------------------------- /spec-opal/before_hooks_spec.rb: -------------------------------------------------------------------------------- 1 | # await: *await* 2 | 3 | require 'spec_helper' 4 | 5 | describe 'hooks' do 6 | describe 'before' do 7 | context 'async' do 8 | let(:raise_before_error) { false } 9 | before do 10 | delay_with_promise 0 do 11 | raise 'problem in before' if raise_before_error 12 | @test_value = 42 13 | end 14 | end 15 | 16 | context 'with sync subject' do 17 | subject { 42 } 18 | 19 | context 'succeeds' do 20 | it { is_expected.to eq @test_value } 21 | end 22 | 23 | context 'before :each fails properly' do 24 | let(:raise_before_error) { true } 25 | 26 | it 'should not reach the example' do 27 | fail 'we reached the example and we should not have!' 28 | end 29 | end 30 | 31 | context 'match fails properly' do 32 | it { is_expected.to_not eq @test_value } 33 | end 34 | 35 | context 'async match' do 36 | it 'succeeds' do 37 | delay_with_promise 0 do 38 | expect(subject).to eq @test_value 39 | end 40 | end 41 | 42 | it 'fails properly' do 43 | delay_with_promise 0 do 44 | expect(subject).to_not eq @test_value 45 | end 46 | end 47 | end 48 | end 49 | 50 | context 'with async subject' do 51 | let(:raise_before_subj_error) { false } 52 | 53 | subject do 54 | delay_with_promise 0 do 55 | raise 'problem in subject' if raise_before_subj_error 56 | 42 57 | end 58 | end 59 | 60 | context 'both succeed' do 61 | it { expect(subject.await).to eq @test_value } 62 | end 63 | 64 | context 'both subject and before(:each) fail properly' do 65 | let(:raise_before_error) { true } 66 | let(:raise_before_subj_error) { true } 67 | 68 | it 'should not reach the example' do 69 | fail 'we reached the example and we should not have!' 70 | end 71 | end 72 | 73 | context 'before :each succeeds, assertion fails properly' do 74 | it { expect(subject.await).to_not eq @test_value } 75 | end 76 | 77 | context 'before :each fails properly' do 78 | let(:raise_before_error) { true } 79 | 80 | it 'should not reach the example' do 81 | fail 'we reached the example and we should not have!' 82 | end 83 | end 84 | 85 | context 'before :each succeeds, subject fails properly' do 86 | let(:raise_before_subj_error) { true } 87 | 88 | it 'should not reach the example' do 89 | fail 'we reached the example and we should not have!' 90 | end 91 | end 92 | 93 | context 'async match' do 94 | it 'succeeds' do 95 | delay_with_promise 0 do 96 | expect(subject.await).to eq @test_value 97 | end 98 | end 99 | 100 | it 'fails properly' do 101 | delay_with_promise 0 do 102 | expect(subject.await).to_not eq @test_value 103 | end 104 | end 105 | end 106 | end 107 | end 108 | 109 | context 'sync' do 110 | context 'with sync subject' do 111 | subject { 42 } 112 | 113 | context 'context' do 114 | context 'success' do 115 | before :context do 116 | $before_context_both_sync = 22 117 | end 118 | 119 | before do 120 | raise "$before_context_both_sync should already be 22!" unless $before_context_both_sync == 22 121 | @test_value = 42 122 | end 123 | 124 | it { is_expected.to eq @test_value } 125 | end 126 | 127 | context 'fails properly' do 128 | before :context do 129 | raise 'it failed in the before context!' 130 | $before_context_both_sync = 55 131 | end 132 | 133 | before do 134 | raise "we reached before:each and we should not have!" if $before_context_both_sync == 55 135 | end 136 | 137 | it 'should not reach the example' do 138 | fail 'we reached the example and we should not have!' 139 | end 140 | end 141 | end 142 | 143 | context 'succeeds' do 144 | before do 145 | @test_value = 42 146 | end 147 | 148 | it { is_expected.to eq @test_value } 149 | end 150 | 151 | context 'before :each fails properly' do 152 | before do 153 | raise 'before :each failed properly' 154 | end 155 | 156 | it 'should not reach the example' do 157 | fail 'we reached the example and we should not have!' 158 | end 159 | end 160 | 161 | context 'first before :each in chain triggers failure' do 162 | before do 163 | raise 'first before :each fails, this is correct' 164 | end 165 | 166 | context 'inner context' do 167 | before do 168 | raise 'we reached the inner before :each and we should not have' 169 | end 170 | 171 | it 'should not reach the example' do 172 | fail 'we reached the example and we should not have!' 173 | end 174 | end 175 | end 176 | 177 | context 'match fails properly' do 178 | before do 179 | @test_value = 42 180 | end 181 | 182 | it { is_expected.to_not eq @test_value } 183 | end 184 | end 185 | end 186 | end 187 | end 188 | -------------------------------------------------------------------------------- /spec-opal/browser-formatter/opal_browser_formatter_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe 'Opal::RSpec::BrowserFormatter' do 2 | context 'group' do 3 | it 'passes' do 4 | expect(42).to eq 42 5 | end 6 | 7 | xit 'a skipped example' do 8 | end 9 | 10 | it 'a failed example' do 11 | expect(42).to eq 43 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec-opal/example_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module SomeHelpers 4 | def opal_rspec_helper 5 | let(:defined_opal_helper) { :it_works } 6 | end 7 | end 8 | 9 | module SomeMoreHelpers 10 | def opal_rspec_include_helper 11 | 42 12 | end 13 | end 14 | 15 | RSpec.configure do |c| 16 | c.extend SomeHelpers 17 | c.include SomeMoreHelpers 18 | end 19 | 20 | describe "RSpec include and extend" do 21 | opal_rspec_helper 22 | 23 | it "works for extend" do 24 | defined_opal_helper.should == :it_works 25 | end 26 | 27 | it "works for include" do 28 | opal_rspec_include_helper.should == 42 29 | end 30 | end 31 | 32 | $count = 0 33 | 34 | describe "let" do 35 | let(:count) { $count += 1 } 36 | 37 | it "memoizes the value" do 38 | count.should eq(1) 39 | count.should eq(1) 40 | end 41 | 42 | it "is not cached across examples" do 43 | count.should eq(2) 44 | end 45 | end 46 | 47 | describe "helper methods" do 48 | def some_helper 49 | :present 50 | end 51 | 52 | it "should be available" do 53 | some_helper.should eq(:present) 54 | end 55 | 56 | describe "nested group" do 57 | it "should work in nested groups" do 58 | some_helper.should eq(:present) 59 | end 60 | end 61 | end 62 | 63 | describe "nested describes" do 64 | it "works in multiple places" do 65 | 1.should eq(1) 66 | end 67 | 68 | describe "nested" do 69 | it "and here" do 70 | 1.should_not eq(2) 71 | end 72 | end 73 | end 74 | 75 | describe "subject" do 76 | subject { [1, 2, 3] } 77 | 78 | it "a new instance should be the subject" do 79 | subject.should be_kind_of(Array) 80 | end 81 | 82 | describe "nested subjects" do 83 | before { subject << 4 } 84 | 85 | it "should work with before and example" do 86 | subject.should == [1, 2, 3, 4] 87 | end 88 | end 89 | end 90 | 91 | describe Hash do 92 | it "should create a new instance of subject for classes" do 93 | subject.should == {} 94 | end 95 | 96 | it "provides the subject as the described_class" do 97 | expect(described_class).to eq(Hash) 98 | end 99 | end 100 | 101 | describe [1, 2, 3] do 102 | it "can use an object instance as a subject" do 103 | expect(subject).to eq([1, 2, 3]) 104 | end 105 | end 106 | 107 | describe "Simple expectations" do 108 | before do 109 | @bar = 200 110 | end 111 | 112 | it "should eat" do 113 | @bar.should == 200 114 | end 115 | 116 | after do 117 | @bar.class 118 | end 119 | end 120 | 121 | describe "should syntax" do 122 | it "should work for positive" do 123 | [1, 2, 3].should == [1, 2, 3] 124 | end 125 | 126 | it "should work for negative" do 127 | [1, 2, 3].should_not == [4, 5, 6] 128 | end 129 | end 130 | 131 | describe "expect syntax" do 132 | it "positive expectation" do 133 | expect(100).to eq(100) 134 | end 135 | 136 | it "negative expectation" do 137 | expect(100).to_not eq(300) 138 | end 139 | end 140 | 141 | describe "Normal errors" do 142 | it "should still work" do 143 | lambda { raise "wtf son" }.should raise_error(Exception) 144 | end 145 | end 146 | 147 | describe "let on an inner scope" do 148 | describe "inner context" do 149 | let(:foo) { :bar } 150 | 151 | it "should still work" do 152 | foo.should eq(:bar) 153 | end 154 | end 155 | end 156 | 157 | describe "#context" do 158 | context "inner context" do 159 | let(:foo) { :bar } 160 | 161 | it "should still work" do 162 | foo.should eq(:bar) 163 | end 164 | end 165 | end 166 | 167 | describe 'exception handling' do 168 | it "should fail properly if an exception is raised" do 169 | raise 'problem' 170 | end 171 | 172 | it "should ignore an exception after a failed assertion" do 173 | expect(42).to eq(43) 174 | raise 'problem' 175 | end 176 | end 177 | -------------------------------------------------------------------------------- /spec-opal/matchers_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "be_truthy" do 4 | it "passes with truthy values" do 5 | expect(true).to be_truthy 6 | expect(1.0).to be_truthy 7 | expect([]).to be_truthy 8 | end 9 | 10 | it 'fails properly with truthy values' do 11 | expect(false).to be_truthy 12 | end 13 | 14 | it "fails with falsey values" do 15 | expect { 16 | expect(false).to be_truthy 17 | }.to raise_error(Exception) 18 | 19 | expect { 20 | expect(nil).to be_truthy 21 | }.to raise_error(Exception) 22 | end 23 | end 24 | 25 | describe "be_falsey" do 26 | it "passes with falsey values" do 27 | expect(false).to be_falsey 28 | expect(nil).to be_falsey 29 | end 30 | 31 | it "fails with truthy values" do 32 | expect { 33 | expect(true).to be_falsey 34 | }.to raise_error(Exception) 35 | 36 | expect { 37 | expect({}).to be_falsey 38 | }.to raise_error(Exception) 39 | end 40 | end 41 | 42 | describe "be_nil" do 43 | it "passes when object is nil" do 44 | expect(nil).to be_nil 45 | end 46 | 47 | it "fails with any other object" do 48 | expect { 49 | expect(false).to be_nil 50 | }.to raise_error(Exception) 51 | 52 | expect { 53 | expect(:foo).to be_nil 54 | }.to raise_error(Exception) 55 | end 56 | end 57 | 58 | describe "be_kind_of" do 59 | it "passes if actual is kind of expected class" do 60 | expect("foo").to be_kind_of(String) 61 | expect("foo").to_not be_kind_of(Numeric) 62 | end 63 | 64 | it "passes if actual is kind of superclass of expected class" do 65 | expect([]).to be_kind_of(Object) 66 | end 67 | 68 | it "fails if expected is not a kind of expected" do 69 | expect { 70 | expect("foo").to be_kind_of(Integer) 71 | }.to raise_error(Exception) 72 | 73 | expect { 74 | expect("foo").to_not be_kind_of(String) 75 | }.to raise_error(Exception) 76 | end 77 | end 78 | 79 | describe "eq" do 80 | it "matches when actual == expected" do 81 | expect(:foo).to eq(:foo) 82 | end 83 | 84 | it "does not match when actual != expected" do 85 | expect(:foo).not_to eq(42) 86 | end 87 | 88 | it "fails if matcher does not match" do 89 | expect { 90 | expect(:foo).to eq(42) 91 | }.to raise_error(Exception) 92 | 93 | expect { 94 | expect(:foo).not_to eq(:foo) 95 | }.to raise_error(Exception) 96 | end 97 | end 98 | 99 | describe "eql" do 100 | it "matches when expected.eql?(actual)" do 101 | expect(1).to eql(1) 102 | end 103 | 104 | it "does not match when !expected.eql?(actual)" do 105 | expect(1).to_not eql(:foo) 106 | end 107 | 108 | it "fails if matcher does not match" do 109 | expect { 110 | expect(1).to eql(:bar) 111 | }.to raise_error(Exception) 112 | 113 | expect { 114 | expect(2).to_not eql(2) 115 | }.to raise_error(Exception) 116 | end 117 | end 118 | 119 | describe "include" do 120 | it "matches if actual includes expected" do 121 | expect("foo").to include("f") 122 | expect([:foo, :bar, :baz]).to include(:baz) 123 | expect({ :yellow => 'lorry' }).to include(:yellow) 124 | end 125 | 126 | it "does not match if actual does not inlcude expected" do 127 | expect("foo").to_not include("b") 128 | expect([:foo, :bar, :baz]).to_not include(:kapow) 129 | expect({ :yellow => 'lorry' }).to_not include(:red) 130 | end 131 | 132 | it "fails if matcher does not match" do 133 | expect { 134 | expect("bar").to include("z") 135 | }.to raise_error(Exception) 136 | end 137 | end 138 | 139 | describe "respond_to" do 140 | it "matches if actual responds to sym" do 141 | expect("foo").to respond_to(:upcase) 142 | end 143 | 144 | it "does not match if actual does not respond to sym" do 145 | expect(Object.new).to_not respond_to(:upcase) 146 | end 147 | 148 | it "fails if actual does not respond to sym" do 149 | expect { 150 | expect(Object.new).to respond_to(:upcase) 151 | }.to raise_error(Exception) 152 | end 153 | end 154 | 155 | describe "match" do 156 | it "matches if actual matches expected" do 157 | expect("foobar").to match(/ar/) 158 | expect("foobar").to match("oob") 159 | end 160 | 161 | it "does not match if actual does not match expected" do 162 | expect("foobar").to_not match(/baz/) 163 | expect("foobar").to_not match("woosh") 164 | end 165 | 166 | it "fails unless matcher matches" do 167 | expect { 168 | exprct("hello").to match(/world/) 169 | }.to raise_error(Exception) 170 | end 171 | end 172 | 173 | describe "operator ==" do 174 | it "matches if actual == expected" do 175 | "hello".should == "hello" 176 | end 177 | 178 | it "does not match when actual does not == expected" do 179 | "hello".should_not == "world" 180 | end 181 | 182 | it "fails unless matcher matches" do 183 | expect { 184 | "hello".should == "world" 185 | }.to raise_error(Exception) 186 | end 187 | end 188 | 189 | class PredicateTest 190 | def foo? 191 | true 192 | end 193 | 194 | def bar? 195 | false 196 | end 197 | end 198 | 199 | describe "predicate matchers" do 200 | it "works with positive expectations" do 201 | expect(PredicateTest.new).to be_foo 202 | end 203 | 204 | it "work with negative expectations" do 205 | expect(PredicateTest.new).to_not be_bar 206 | end 207 | end 208 | -------------------------------------------------------------------------------- /spec-opal/mock_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "RSpec mocks" do 4 | describe "stubs" do 5 | it "can stub basic methods" do 6 | obj = Object.new 7 | expect(obj).to receive(:foo) { 100 } 8 | obj.foo.should == 100 9 | end 10 | 11 | it "raises an exception when stub returns wrong value" do 12 | expect { 13 | obj = Object.new 14 | expect(obj).to receive(:bar) { 400 } 15 | obj.bar.should == 42 16 | }.to raise_error(Exception) 17 | end 18 | 19 | it "allow" do 20 | obj = Object.new 21 | allow(obj).to receive(:name) { "Adam B" } 22 | allow(obj).to receive(:job).and_return("Eating Fruit Gums") 23 | 24 | expect(obj.name).to eq("Adam B") 25 | expect(obj.job).to eq("Eating Fruit Gums") 26 | end 27 | 28 | it "expecting arguments" do 29 | person = double("person") 30 | expect(person).to receive(:foo).with(4, 5, 6) 31 | person.foo(4, 5, 6) 32 | end 33 | end 34 | 35 | describe "doubles" do 36 | it "define methods on double" do 37 | person = double("person", :name => "Adam") 38 | expect(person.name).to eq("Adam") 39 | end 40 | 41 | it "once" do 42 | person = double("person") 43 | expect(person).to receive(:name).once 44 | person.name.should eq(nil) 45 | end 46 | 47 | it "twice" do 48 | person = double("person") 49 | expect(person).to receive(:name).twice 50 | person.name 51 | person.name.should 52 | end 53 | end 54 | 55 | it "can mock existing methods on objects" do 56 | expect(Time).to receive(:now).once.and_call_original 57 | Time.now.should be_kind_of(Time) 58 | end 59 | 60 | describe 'stubs' do 61 | it 'works and displays deprecation' do 62 | Object.new.stub :foo 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /spec-opal/other/color_on_by_default_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec::configure do |c| 2 | c.color = true 3 | end 4 | 5 | describe 'colors' do 6 | subject { 42 } 7 | 8 | it { is_expected.to eq 42 } 9 | end 10 | -------------------------------------------------------------------------------- /spec-opal/other/dummy_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'foobar' do 2 | subject { 42 } 3 | 4 | it { is_expected.to eq 42 } 5 | end 6 | -------------------------------------------------------------------------------- /spec-opal/other/formatter_dependency.rb: -------------------------------------------------------------------------------- 1 | module FormatterDependency 2 | 3 | end 4 | -------------------------------------------------------------------------------- /spec-opal/other/ignored_spec.opal: -------------------------------------------------------------------------------- 1 | describe 'nope' do 2 | subject { 42 } 3 | 4 | it { is_expected.to eq 42 } 5 | end 6 | -------------------------------------------------------------------------------- /spec-opal/other/test_formatter.rb: -------------------------------------------------------------------------------- 1 | class TestFormatter < ::RSpec::Core::Formatters::JsonFormatter 2 | include FormatterDependency 3 | ::RSpec::Core::Formatters.register self, :message, :dump_summary, :dump_profile, :stop, :close 4 | 5 | def close(_notification) 6 | super 7 | output.write 'test formatter ran!' 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec-opal/should_syntax_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "One-liner should syntax" do 4 | subject { 42 } 5 | 6 | describe "should" do 7 | it { should == 42 } 8 | it { should_not == 43 } 9 | end 10 | 11 | describe "is_expected" do 12 | it { is_expected.to eq(42) } 13 | it { is_expected.to_not eq(43) } 14 | end 15 | 16 | describe "expect" do 17 | it { expect(42).to eq(42) } 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec-opal/skip_pending_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'skip' do 4 | it 'in example, no promise' do 5 | skip 'want to skip within' 6 | end 7 | 8 | it 'no implementation provided' 9 | 10 | skip 'entire group' do 11 | it 'example 1' do 12 | 1.should == 2 13 | end 14 | 15 | it 'example 2' do 16 | 1.should == 3 17 | end 18 | end 19 | 20 | xit 'via xit' do 21 | 1.should == 3 22 | end 23 | 24 | it 'via variable', skip: true do 25 | 1.should == 3 26 | end 27 | end 28 | 29 | describe 'pending' do 30 | context 'in example' do 31 | context 'no promise' do 32 | it 'would fail otherwise' do 33 | pending 'want to pend within example' 34 | obj = [1, 2, 3, 4] 35 | obj.should == [2, 2, 3, 4] 36 | end 37 | 38 | it 'would not fail otherwise, thus fails properly' do 39 | pending 'want to pend within example' 40 | obj = [1, 2, 3, 4] 41 | obj.should == [1, 2, 3, 4] 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec-opal/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'opal/rspec/async' 2 | 3 | RSpec::configure do |config| 4 | config.filter_run_including focus: true 5 | config.run_all_when_everything_filtered = true 6 | config.color = true 7 | end 8 | -------------------------------------------------------------------------------- /spec-opal/sprockets_runner_js_errors.rb.erb: -------------------------------------------------------------------------------- 1 | # backtick_javascript: true 2 | 3 | # This file is used by Opal::Server to basically load all spec files that 4 | # can be found in the spec/ directory. 5 | 6 | 7 | # Hard to capture errors w/ Selenium 8 | %x{ 9 | window.jsErrors = []; 10 | window.onerror = function (errorMessage) { 11 | window.jsErrors.push(errorMessage); 12 | }; 13 | } 14 | 15 | require 'opal/rspec/autorun' 16 | require 'opal/rspec/spec_opts' 17 | 18 | #ERB <% environment.get_opal_spec_requires.each do |s| %> 19 | require <%= s.inspect %> 20 | #ERB <% end %> 21 | 22 | ::Kernel.exit 23 | -------------------------------------------------------------------------------- /spec-opal/subject_spec.rb: -------------------------------------------------------------------------------- 1 | # await: *await* 2 | 3 | require 'spec_helper' 4 | 5 | describe 'subject' do 6 | context 'sync' do 7 | context 'named' do 8 | subject(:named_subject) { [1, 2, 3] } 9 | 10 | it "should be the subject" do 11 | subject.should be_kind_of(Array) 12 | end 13 | 14 | it "should be the named subject" do 15 | subject.should eql(named_subject) 16 | end 17 | end 18 | 19 | context 'unnamed' do 20 | subject { 42 } 21 | 22 | context 'passes' do 23 | it { is_expected.to eq 42 } 24 | end 25 | 26 | context 'assertion fails properly' do 27 | it { is_expected.to eq 43 } 28 | end 29 | 30 | context 'fails properly during subject create' do 31 | subject do 32 | raise 'did not work' 33 | end 34 | 35 | it { is_expected.to eq 42 } 36 | end 37 | end 38 | end 39 | 40 | context 'async' do 41 | describe 'assertion' do 42 | subject do 43 | delay_with_promise 0 do 44 | 42 45 | end 46 | end 47 | 48 | context 'explicit async' do 49 | it 'passes' do 50 | delay_with_promise 0 do 51 | expect(subject.await).to eq 42 52 | end 53 | end 54 | 55 | it 'fails properly' do 56 | delay_with_promise 0 do 57 | expect(subject.await).to eq 43 58 | end 59 | end 60 | end 61 | 62 | context 'implicit' do 63 | context 'passes' do 64 | it { expect(subject.await).to eq 42 } 65 | end 66 | 67 | context 'fails properly' do 68 | it { expect(subject.await).to eq 43 } 69 | end 70 | end 71 | end 72 | 73 | context 'fails properly during creation' do 74 | subject do 75 | delay_with_promise 0 do 76 | raise 'did not work' 77 | end 78 | end 79 | 80 | context 'implicit usage' do 81 | it { expect(subject.await).to eq 42 } 82 | end 83 | 84 | it 'explicit async' do 85 | delay_with_promise 0 do 86 | expect(subject.await).to eq 42 87 | end 88 | end 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /spec/integration/browser_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe 'browser formatter', type: :feature do 4 | before do 5 | file = "#{__dir__}/browser_spec.ru" 6 | Capybara.app = Rack::Builder.new_from_string(File.read(file), file) 7 | end 8 | 9 | before do 10 | visit '/' 11 | # Specs should run in 12 seconds but in case Travis takes longer, provide some cushion 12 | Capybara.default_max_wait_time = 40 13 | end 14 | 15 | after do 16 | js_errors = page.evaluate_script('window.jsErrors') || [] 17 | puts "Javascript errors: #{js_errors}" if js_errors.any? 18 | end 19 | 20 | it 'matches test results' do 21 | expect(page.find('h1')).to have_content 'RSpec Code Examples' 22 | expect(page.find('#totals')).to have_content '136 examples, 37 failures, 13 pending' 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/integration/browser_spec.ru: -------------------------------------------------------------------------------- 1 | require 'opal/rspec/sprockets' 2 | require 'opal-sprockets' 3 | require 'opal/sprockets/server' 4 | 5 | root = File.expand_path("#{__dir__}/../..") 6 | 7 | Opal::Config.source_map_enabled = false 8 | sprockets = Opal::RSpec::SprocketsEnvironment.new('spec-opal/*_spec.{rb,opal}') 9 | sprockets.add_spec_paths_to_sprockets 10 | 11 | run Opal::Sprockets::Server.new(sprockets: sprockets) { |s| 12 | s.main = 'sprockets_runner_js_errors' 13 | s.debug = ENV['OPAL_DEBUG'] 14 | } 15 | -------------------------------------------------------------------------------- /spec/integration/spec_opts_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'json' 3 | 4 | RSpec.describe 'spec_opts' do 5 | let(:rake_task) { 'other_specs' } 6 | let(:config_opts) { nil } 7 | subject(:output) { `SPEC_OPTS="#{spec_opts}" rake #{rake_task}` } 8 | 9 | RSpec.shared_context :color_test do |expected_pass| 10 | it do 11 | File.write(".rspec-opal", config_opts) if config_opts 12 | matcher = match Regexp.new Regexp.escape("\e[32m1 example, 0 failures\e[0m") 13 | exp = is_expected 14 | expected_pass ? exp.to(matcher) : exp.to_not(matcher) 15 | ensure 16 | File.unlink(".rspec-opal") if config_opts 17 | end 18 | end 19 | 20 | context 'color set' do 21 | let(:spec_opts) { '--color' } 22 | 23 | include_context :color_test, true 24 | end 25 | 26 | context 'color set via config file' do 27 | let(:config_opts) { '--color' } 28 | let(:spec_opts) { '' } 29 | 30 | include_context :color_test, true 31 | end 32 | 33 | context 'color set via config file but unset via command line' do 34 | let(:config_opts) { '--color' } 35 | let(:spec_opts) { '--no-color' } 36 | 37 | include_context :color_test, false 38 | end 39 | 40 | context 'no color explicitly set' do 41 | let(:spec_opts) { '--no-color' } 42 | let(:rake_task) { 'color_on_by_default' } 43 | 44 | it { is_expected.to match /1 example, 0 failures/ } 45 | 46 | include_context :color_test, false 47 | end 48 | 49 | context 'formatter set' do 50 | let(:spec_opts) { '--format json' } 51 | let(:expected_json_hash) do 52 | { 53 | 'examples' => 54 | [ 55 | { 56 | 'description' => 'is expected to eq 42', 57 | 'full_description' => 'foobar is expected to eq 42', 58 | 'status' => 'passed', 59 | 'file_path' => be_a(String), 60 | 'id' => be_a(String), 61 | 'line_number' => be_a(Integer), 62 | 'run_time' => be_a(Float), 63 | 'pending_message' => nil, 64 | } 65 | ], 66 | 'summary' => { 67 | 'duration' => be_a(Float), 68 | 'errors_outside_of_examples_count' => 0, 69 | 'example_count' => 1, 70 | 'failure_count' => 0, 71 | 'pending_count' => 0 72 | }, 73 | 'summary_line' => '1 example, 0 failures', 74 | 'version' => be_a(String) 75 | } 76 | end 77 | 78 | subject { JSON.parse(/(\{.*)/.match(output).captures[0]) } 79 | 80 | it { is_expected.to include expected_json_hash } 81 | end 82 | 83 | context 'empty' do 84 | let(:spec_opts) { '' } 85 | 86 | it { is_expected.to match /1 example, 0 failures/ } 87 | 88 | include_context :color_test, true 89 | end 90 | 91 | context 'requires and format' do 92 | let(:spec_opts) { '-Ispec-opal/other --format TestFormatter --require formatter_dependency --require test_formatter' } 93 | 94 | it { is_expected.to match /{.*"examples".*test formatter ran!/m } 95 | end 96 | 97 | context 'default' do 98 | subject { `rake other_specs` } 99 | 100 | it { is_expected.to match /1 example, 0 failures/ } 101 | 102 | include_context :color_test, true 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /spec/integration/verify_other_specs_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe 'Verify spec-opal/other' do 4 | describe 'dummy_spec.rb' do 5 | context 'using CLI' do 6 | it 'runs correctly' do 7 | test_output = `opal-rspec spec-opal/other/dummy_spec.rb 2> /dev/null` 8 | expect($?.exitstatus).to eq(0) 9 | expect(test_output).to match(/1 example, 0 failures/) 10 | end 11 | end 12 | 13 | context 'using Rake task' do 14 | it 'runs correctly' do 15 | test_output = `rake spec:opal PATTERN="spec-opal/other/dummy_spec.rb" 2> /dev/null` 16 | expect($?.exitstatus).to eq(0) 17 | expect(test_output).to match(/1 example, 0 failures/) 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/integration/verify_rspec_specs_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe 'RSpec specs:' do 4 | 5 | def expect_results_to_be(*expected_summaries) 6 | results = Opal::RSpec::UpstreamTests::Runner.new.run 7 | failures = results.json[:examples].select { |ex| ex[:status] == 'failed' } 8 | print_results(results) unless failures.empty? 9 | 10 | expect(expected_summaries).to include(results.json[:summary_line]) 11 | expect(failures).to eq([]) 12 | expect(results).to be_successful 13 | rescue => e 14 | print_results(results) 15 | raise e 16 | end 17 | 18 | def print_results(results) 19 | return if results.nil? 20 | puts "=========== Output of failed run ============" 21 | puts results.quoted_output 22 | puts "=============================================" 23 | end 24 | 25 | context 'Core' do 26 | it 'runs correctly', gem_name: 'rspec-core' do 27 | expect_results_to_be('1622 examples, 0 failures, 393 pending') 28 | end 29 | end 30 | 31 | context 'Support' do 32 | it 'runs correctly', gem_name: 'rspec-support' do 33 | expect_results_to_be('181 examples, 0 failures, 32 pending') 34 | end 35 | end 36 | 37 | context 'Expectations' do 38 | it 'runs correctly', gem_name: 'rspec-expectations' do 39 | expect_results_to_be('1798 examples, 0 failures, 362 pending') 40 | end 41 | end 42 | 43 | context 'Mocks' do 44 | it 'runs correctly', gem_name: 'rspec-mocks' do 45 | # There are different results on CI for some reason 46 | expect_results_to_be('1645 examples, 0 failures, 475 pending', '1655 examples, 0 failures, 485 pending') 47 | end 48 | end 49 | 50 | context 'Diff-LCS' do 51 | it 'runs correctly', gem_name: 'diff-lcs' do 52 | expect_results_to_be('272 examples, 0 failures') 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/opal/rspec/browser_formatter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe 'Opal::RSpec::BrowserFormatter', type: :feature, js: true do 4 | # Use Rack config exactly as shipped in the GEM 5 | before do 6 | file = "#{__dir__}/browser_formatter_spec.ru" 7 | Capybara.app = Rack::Builder.new_from_string(File.read(file), file) 8 | end 9 | 10 | let(:error_fetcher) { page.evaluate_script('window.jsErrors') } 11 | 12 | before do 13 | visit '/' 14 | # Specs should run in 12 seconds but in case Travis takes longer, provide some cushion 15 | Capybara.default_max_wait_time = 40 16 | end 17 | 18 | it 'matches test results' do 19 | expect(page).to have_content '3 examples, 1 failure, 1 pending' 20 | expect(page).to have_content 'group' 21 | expect(page).to have_content 'a skipped example' 22 | expect(page).to have_content 'a failed example' 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/opal/rspec/browser_formatter_spec.ru: -------------------------------------------------------------------------------- 1 | require 'opal/rspec/sprockets' 2 | require 'opal/sprockets' 3 | require 'opal/sprockets/server' 4 | 5 | Opal::Config.source_map_enabled = false 6 | Opal::Config.arity_check_enabled = true 7 | 8 | sprockets = Opal::RSpec::SprocketsEnvironment.new('spec-opal/browser-formatter/*_spec.{rb,opal}') 9 | sprockets.cache = ::Sprockets::Cache::FileStore.new('tmp/cache/opal_specs') 10 | sprockets.add_spec_paths_to_sprockets 11 | 12 | run Opal::Sprockets::Server.new(sprockets: sprockets) { |s| 13 | s.main = 'sprockets_runner_js_errors' 14 | # sprockets_runner_js_errors will not be in the opal load path by default 15 | # s.append_path 'spec/integration/rack' 16 | s.debug = true 17 | } 18 | -------------------------------------------------------------------------------- /spec/opal/rspec/cached_environment_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | require 'opal/rspec/cached_environment' 3 | require 'opal/rspec/sprockets_environment' 4 | require 'opal/rspec/temp_dir_helper' 5 | 6 | RSpec.describe Opal::RSpec::CachedEnvironment do 7 | let(:pattern) { nil } 8 | let(:exclude_pattern) { nil } 9 | let(:default_path) { nil } 10 | let(:files) { nil } 11 | include_context :temp_dir 12 | 13 | let(:original_env) { Opal::RSpec::SprocketsEnvironment.new pattern, exclude_pattern, files, default_path } 14 | 15 | subject(:env) do 16 | # in subject to allow contexts to execute before logic 17 | original_env.add_spec_paths_to_sprockets 18 | original_env.cached 19 | end 20 | 21 | describe '#get_opal_spec_requires' do 22 | subject { env.get_opal_spec_requires.sort } 23 | 24 | context 'no default path set' do 25 | before do 26 | create_dummy_spec_files 'spec-opal/foobar/dummy_spec.rb', 'spec-opal/foobar/ignored_spec.opal' 27 | end 28 | 29 | let(:pattern) { 'spec-opal/foobar/**/*_spec.rb' } 30 | 31 | it { is_expected.to eq ['foobar/dummy_spec'] } 32 | end 33 | 34 | context 'default path set' do 35 | before do 36 | create_dummy_spec_files 'spec-opal/foobar/dummy_spec.rb', 'spec-opal/foobar/ignored_spec.opal' 37 | end 38 | 39 | let(:pattern) { 'spec-opal/foobar/**/*_spec.rb' } 40 | let(:default_path) { 'spec-opal/foobar' } 41 | 42 | it { is_expected.to eq ['dummy_spec'] } 43 | end 44 | 45 | context 'multiple pattern' do 46 | before do 47 | create_dummy_spec_files 'spec-opal/foobar/hello1_spec.rb', 'spec-opal/foobar/hello2_spec.rb', 'spec-opal/foobar/bye1_spec.rb', 'spec-opal/foobar/bye2_spec.rb' 48 | end 49 | 50 | let(:pattern) { %w(**/*/*1_spec.rb **/*/bye*_spec.rb) } 51 | 52 | it { is_expected.to eq %w(foobar/bye1_spec foobar/bye2_spec foobar/hello1_spec) } 53 | end 54 | 55 | context 'exclude pattern' do 56 | before do 57 | create_dummy_spec_files 'spec-opal/foobar/hello1_spec.rb', 'spec-opal/foobar/hello2_spec.rb', 'spec-opal/foobar/bye1_spec.rb', 'spec-opal/foobar/bye2_spec.rb' 58 | end 59 | 60 | let(:pattern) { 'spec-opal/**/*_spec.rb' } 61 | 62 | context 'single' do 63 | let(:exclude_pattern) { '**/*/*1_spec.rb' } 64 | 65 | it { is_expected.to eq %w(foobar/bye2_spec foobar/hello2_spec) } 66 | end 67 | 68 | context 'multiple' do 69 | let(:exclude_pattern) { %w(**/*/*1_spec.rb **/*/bye*_spec.rb) } 70 | 71 | it { is_expected.to eq ['foobar/hello2_spec'] } 72 | end 73 | end 74 | 75 | context 'files' do 76 | before do 77 | create_dummy_spec_files 'spec-opal/foobar/hello1_spec.rb', 'spec-opal/foobar/hello2_spec.rb', 'spec-opal/foobar/bye1_spec.rb', 'spec-opal/foobar/bye2_spec.rb' 78 | end 79 | 80 | let(:files) { FileList['spec-opal/**/h*_spec.rb'] } 81 | 82 | it { is_expected.to eq %w(foobar/hello1_spec foobar/hello2_spec) } 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /spec/opal/rspec/locator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Opal::RSpec::Locator do 4 | let(:root_path) { File.expand_path("#{__dir__}/../../..") } 5 | 6 | context 'with default args' do 7 | specify '#determine_root' do 8 | expect(subject.determine_root.to_s).to eq(root_path) 9 | end 10 | 11 | specify '#get_spec_load_paths' do 12 | expect(subject.get_spec_load_paths).to eq(["#{root_path}/spec-opal"]) 13 | end 14 | 15 | specify '#get_opal_spec_requires' do 16 | expect(subject.get_opal_spec_requires.sort).to eq([ 17 | "#{root_path}/spec-opal/after_hooks_spec.rb", 18 | "#{root_path}/spec-opal/around_hooks_spec.rb", 19 | "#{root_path}/spec-opal/async_spec.rb", 20 | "#{root_path}/spec-opal/before_hooks_spec.rb", 21 | "#{root_path}/spec-opal/browser-formatter/opal_browser_formatter_spec.rb", 22 | "#{root_path}/spec-opal/example_spec.rb", 23 | "#{root_path}/spec-opal/matchers_spec.rb", 24 | "#{root_path}/spec-opal/mock_spec.rb", 25 | "#{root_path}/spec-opal/other/color_on_by_default_spec.rb", 26 | "#{root_path}/spec-opal/other/dummy_spec.rb", 27 | "#{root_path}/spec-opal/should_syntax_spec.rb", 28 | "#{root_path}/spec-opal/skip_pending_spec.rb", 29 | "#{root_path}/spec-opal/subject_spec.rb", 30 | "#{root_path}/spec-opal/other/ignored_spec.opal", 31 | ].sort) 32 | end 33 | end 34 | 35 | context 'with a set default_path' do 36 | subject { described_class.new(default_path: 'lib/opal') } 37 | 38 | specify '#determine_root' do 39 | expect(subject.determine_root.to_s).to eq(root_path) 40 | end 41 | 42 | specify '#get_spec_load_paths' do 43 | expect(subject.get_spec_load_paths).to eq(["#{root_path}/lib/opal"]) 44 | end 45 | 46 | specify '#get_opal_spec_requires' do 47 | # There are no spec files under lib/opal. 48 | expect(subject.get_opal_spec_requires.sort).to eq([].sort) 49 | end 50 | end 51 | 52 | context 'with a set pattern' do 53 | subject { described_class.new(default_path: 'lib/opal', pattern: 'lib/opal/*.rb') } 54 | 55 | specify '#get_opal_spec_requires' do 56 | expect(subject.get_opal_spec_requires).to eq(["#{root_path}/lib/opal/rspec.rb"]) 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/opal/rspec/rake_task_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | require 'opal/rspec/rake_task' 3 | require 'rack' 4 | require 'opal/rspec/temp_dir_helper' 5 | 6 | RSpec.describe Opal::RSpec::RakeTask do 7 | before { Rake::Task.tasks.each(&:clear).each(&:reenable) } 8 | 9 | it 'exits with the result of #run' do 10 | exitcode = 1 11 | runner_double = instance_double(Opal::RSpec::Runner, run: exitcode) 12 | task_builder = described_class.new 13 | expect(task_builder).to receive(:exit).with(exitcode) 14 | task_builder.rake_task.invoke 15 | end 16 | 17 | context 'with a block' do 18 | let(:runner_double) { instance_double(Opal::RSpec::Runner, run: 0) } 19 | let(:block) { -> a,b { :foobar } } 20 | 21 | xit 'forwards the block to Runner#command' do 22 | expect(Opal::RSpec::Runner).to receive(:new) do |&received_block| 23 | expect(received_block).to eq(block) 24 | runner_double 25 | end 26 | 27 | task_builder = described_class.new(&block) 28 | expect(task_builder).to receive(:sh).with(runner_double.command) 29 | task_builder.rake_task.invoke 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/opal/rspec/runner_spec.rb: -------------------------------------------------------------------------------- 1 | require 'shellwords' 2 | require 'spec_helper' 3 | require 'rspec' 4 | require 'opal/rspec/runner' 5 | require 'opal/rspec/temp_dir_helper' 6 | 7 | RSpec.describe Opal::RSpec::Runner do 8 | include_context :temp_dir 9 | 10 | # Keep ENV['RUNNER'] clean (in case we're running on travis, etc.) 11 | around do |example| 12 | current_env_runner = ENV['RUNNER'] 13 | ENV['RUNNER'] = nil 14 | example.run 15 | ENV['RUNNER'] = current_env_runner 16 | end 17 | 18 | RSpec::Matchers.define :invoke_runner do |expected, timeout_value=nil| 19 | expected_string = " --runner #{expected} " 20 | match do |actual| 21 | expect(actual.command+' ').to include(expected_string) 22 | end 23 | 24 | failure_message do |actual| 25 | "expected #{actual.command.inspect} to include #{expected_string.inspect}" 26 | end 27 | end 28 | 29 | RSpec::Matchers.define :require_opal_specs do |*expected_paths| 30 | expected_string = nil 31 | match do |actual| 32 | expected_paths.each do |expected_path| 33 | expected_string = " -p#{expected_path}" 34 | expect(actual.command).to include(expected_string) 35 | end 36 | end 37 | 38 | failure_message do |actual| 39 | "expected #{actual.command.inspect} to include #{expected_string.inspect}" 40 | end 41 | end 42 | 43 | RSpec::Matchers.define :append_opal_path do |expected_path| 44 | expected_string = " -I#{expected_path} " 45 | match do |actual| 46 | expect(actual.command+' ').to include(expected_string) 47 | end 48 | 49 | failure_message do |actual| 50 | "expected #{actual.command.inspect} to include #{expected_string.inspect}" 51 | end 52 | end 53 | 54 | context 'default options' do 55 | before { create_dummy_spec_files 'spec-opal/something/dummy_spec.rb' } 56 | let(:command) { subject.command } 57 | 58 | it 'has default options' do 59 | expect(command).not_to include(' -R') 60 | expect(command).not_to include(' --runner') 61 | expect(command).to include(" -I#{temp_dir}/spec-opal ") 62 | expect(subject).to require_opal_specs("#{temp_dir}/spec-opal/something/dummy_spec.rb") 63 | end 64 | end 65 | 66 | context 'explicit runner' do 67 | before { create_dummy_spec_files 'spec-opal/something/dummy_spec.rb' } 68 | 69 | RSpec.shared_context :explicit do |expected_runner| 70 | it 'sets the options' do 71 | expect(subject).to have_attributes pattern: nil 72 | expect(subject).to append_opal_path "#{temp_dir}/spec-opal" 73 | expect(subject).to require_opal_specs("#{temp_dir}/spec-opal/something/dummy_spec") 74 | expect(subject).to invoke_runner expected_runner 75 | end 76 | end 77 | 78 | TEST_RUNNERS = [:chrome, :node, :server] 79 | 80 | context 'setting runner via ENV' do 81 | TEST_RUNNERS.each do |runner| 82 | context "as #{runner}" do 83 | before do 84 | ENV['RUNNER'] = runner.to_s 85 | end 86 | subject { described_class.new } 87 | 88 | include_context :explicit, runner 89 | end 90 | end 91 | end 92 | 93 | context 'Rake task' do 94 | TEST_RUNNERS.each do |runner| 95 | context "as #{runner}" do 96 | subject do 97 | described_class.new do |_, runner_| 98 | runner_.runner = runner 99 | end 100 | end 101 | 102 | include_context :explicit, runner 103 | end 104 | end 105 | end 106 | end 107 | 108 | context 'pattern' do 109 | subject { described_class.new { |_, task| task.pattern = 'spec-opal/other/**/*_spec.rb' } } 110 | before { create_dummy_spec_files 'spec-opal/other/foo_spec.rb', 'spec-opal/other/bar_spec.rb', 'spec-opal/other/test_formatter.rb' } 111 | 112 | it { expect(subject.command).to include("spec-opal/other/foo_spec.rb") } 113 | it { expect(subject.command).to include("spec-opal/other/bar_spec.rb") } 114 | it { expect(subject.command).not_to include("spec-opal/other/test_formatter.rb") } 115 | it { expect(subject.command).not_to include("spec-opal/other/color_on_by_default_spec.rb") } 116 | it { expect(subject.command).not_to include("spec-opal/other/formatter_dependency.rb") } 117 | it { expect(subject.command).not_to include("spec-opal/other/ignored_spec.opal") } 118 | it { is_expected.to append_opal_path "#{temp_dir}/spec-opal" } 119 | end 120 | 121 | context 'default path' do 122 | subject { described_class.new { |_, task| task.pattern = 'spec-opal/other/**/*_spec.rb'; task.default_path = 'spec-opal/other' } } 123 | before { create_dummy_spec_files 'spec-opal/other/foo_spec.rb', 'spec-opal/other/bar_spec.rb', 'spec-opal/other/test_formatter.rb' } 124 | 125 | it { is_expected.to append_opal_path "#{temp_dir}/spec-opal/other" } 126 | it { expect(subject.command).to include("spec-opal/other/foo_spec.rb") } 127 | it { expect(subject.command).to include("spec-opal/other/bar_spec.rb") } 128 | end 129 | 130 | context 'files' do 131 | subject { described_class.new { |_, task| task.files = FileList['spec-opal/other/**/*_spec.rb'] } } 132 | before { create_dummy_spec_files 'spec-opal/other/dummy_spec.rb' } 133 | 134 | it { is_expected.to have_attributes files: FileList['spec-opal/other/**/*_spec.rb'] } 135 | it { is_expected.to append_opal_path "#{temp_dir}/spec-opal" } 136 | it { is_expected.to require_opal_specs "#{temp_dir}/spec-opal/other/dummy_spec.rb" } 137 | end 138 | 139 | context 'pattern and files' do 140 | before { create_dummy_spec_files 'spec-opal/spec_spec.rb' } 141 | 142 | let(:expected_to_run) { false } 143 | let(:files) { FileList['spec-opal/*_spec.rb'] } 144 | let(:pattern) { 'spec-opal/**/*spec_spec.rb' } 145 | subject { described_class.new { |_, task| task.files = files; task.pattern = pattern } } 146 | 147 | it 'cannot accept both files and a pattern' do 148 | expect { subject }.to raise_exception 'Cannot supply both a pattern and files!' 149 | end 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /spec/opal/rspec/sprockets_environment_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | require 'opal/rspec/sprockets_environment' 3 | require 'opal/rspec/temp_dir_helper' 4 | 5 | RSpec.describe Opal::RSpec::SprocketsEnvironment do 6 | include_context :temp_dir 7 | let(:args) { [] } 8 | subject(:env) { Opal::RSpec::SprocketsEnvironment.new *args } 9 | 10 | RSpec::Matchers.define :have_pathnames do |expected| 11 | expected = expected.map { |p| File.expand_path(p) } 12 | 13 | match do |actual| 14 | actual == expected 15 | end 16 | end 17 | 18 | describe '#cached' do 19 | subject { env.cached } 20 | 21 | it { is_expected.to be_a ::Opal::RSpec::CachedEnvironment } 22 | end 23 | 24 | describe '#add_spec_paths_to_sprockets' do 25 | let(:args) { [pattern, nil, nil, default_path] } 26 | let(:default_path) { nil } 27 | 28 | subject do 29 | # in subject to allow contexts to execute before logic 30 | env.add_spec_paths_to_sprockets 31 | env.paths.sort 32 | end 33 | 34 | context 'default path not set' do 35 | before do 36 | create_dummy_spec_files 'spec-opal/foobar/dummy_spec.rb', 'spec-opal/foobar/ignored_spec.opal' 37 | end 38 | 39 | let(:pattern) { 'spec-opal/foobar/**/*_spec.rb' } 40 | 41 | it { is_expected.to have_pathnames ['spec-opal'] } 42 | end 43 | 44 | context 'default path set' do 45 | before do 46 | create_dummy_spec_files 'spec-opal/foobar/dummy_spec.rb', 'spec-opal/foobar/ignored_spec.opal' 47 | end 48 | 49 | let(:pattern) { 'spec-opal/foobar/**/*_spec.rb' } 50 | let(:default_path) { 'spec-opal/foobar' } 51 | 52 | it { is_expected.to have_pathnames ['spec-opal/foobar'] } 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/opal/rspec/temp_dir_helper.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_context :temp_dir do 2 | let(:temp_dir) { File.realpath Dir.mktmpdir } 3 | let!(:current_dir) { Dir.pwd } 4 | before { Dir.chdir temp_dir } 5 | 6 | after do 7 | Dir.chdir current_dir 8 | FileUtils.rm_rf temp_dir 9 | end 10 | 11 | def create_dummy_spec_files(*files) 12 | files.each do |file| 13 | FileUtils.mkdir_p File.dirname(file) 14 | FileUtils.touch file 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/rspec/filter_processor.rb: -------------------------------------------------------------------------------- 1 | module Opal 2 | module RSpec 3 | module OpalVersionStuff 4 | def arity_checking_working? 5 | at_least_opal_0_10? 6 | end 7 | 8 | def at_least_opal_0_10? 9 | greater_equal_than_version?('0.10') 10 | end 11 | 12 | def at_least_opal_0_9? 13 | greater_equal_than_version?('0.9') 14 | end 15 | 16 | def at_least_opal_0_11? 17 | greater_equal_than_version?('0.11') 18 | end 19 | 20 | def greater_equal_than_version?(version) 21 | # it's ok if we have a pre-release version 22 | Gem::Version.new(Opal::VERSION) >= Gem::Version.new("#{version}.0.a") 23 | end 24 | end 25 | 26 | class FilterProcessor 27 | attr_reader :all_filters 28 | attr_accessor :filename 29 | 30 | include OpalVersionStuff 31 | 32 | class GuardCheck 33 | attr_reader :opal_version 34 | 35 | include Opal::RSpec::OpalVersionStuff 36 | 37 | def initialize(current_filters, opal_version) 38 | @current_filters = current_filters 39 | @opal_version = opal_version 40 | end 41 | 42 | def unless(&block) 43 | result = instance_eval(&block) 44 | remove_filter if result 45 | end 46 | 47 | def if(&block) 48 | result = instance_eval(&block) 49 | remove_filter unless result 50 | end 51 | 52 | private 53 | 54 | def remove_filter 55 | @current_filters.pop 56 | nil 57 | end 58 | end 59 | 60 | def initialize 61 | @all_filters = [] 62 | @current_title = nil 63 | @current_filters = [] 64 | end 65 | 66 | def rspec_filter(title) 67 | @current_filters = [] 68 | @current_title = title 69 | yield 70 | @all_filters += @current_filters.map do |filter| 71 | filter.merge({ title: @current_title }) 72 | end 73 | end 74 | 75 | def filter(value) 76 | call_info = caller[0] 77 | line_number = /.*:(\d+)/.match(call_info).captures[0] 78 | @current_filters << { 79 | filename: filename, 80 | line_number: line_number, 81 | exclusion: value 82 | } 83 | GuardCheck.new(@current_filters, opal_version) 84 | end 85 | 86 | private 87 | 88 | def opal_version 89 | Opal::VERSION 90 | end 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /spec/rspec/shared/opal/fixes/deprecation_helpers.rb: -------------------------------------------------------------------------------- 1 | # Since our call site (locating which line a test is on does not yet work, we don't want to fail all of these mocks) 2 | module RSpecHelpers 3 | def expect_deprecation_with_call_site(file, line, snippet=//) 4 | expect(RSpec.configuration.reporter).to receive(:deprecation) do |options| 5 | #expect(options[:call_site]).to include([file, line].join(':')) 6 | expect(options[:deprecated]).to match(snippet) 7 | end 8 | end 9 | 10 | def expect_deprecation_without_call_site(snippet=//) 11 | expect(RSpec.configuration.reporter).to receive(:deprecation) do |options| 12 | #expect(options[:call_site]).to eq nil 13 | expect(options[:deprecated]).to match(snippet) 14 | end 15 | end 16 | 17 | def expect_warn_deprecation_with_call_site(file, line, snippet=//) 18 | expect(RSpec.configuration.reporter).to receive(:deprecation) do |options| 19 | message = options[:message] 20 | expect(message).to match(snippet) 21 | #expect(message).to include([file, line].join(':')) 22 | end 23 | end 24 | 25 | def expect_warning_with_call_site(file, line, expected=//) 26 | expect(::Kernel).to receive(:warn) do |message| 27 | expect(message).to match expected 28 | #expect(message).to match(/Called from #{file}:#{line}/) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/rspec/shared/opal/fixes/rspec_helpers.rb: -------------------------------------------------------------------------------- 1 | # the safety method is defined in helper_methods and uses threads 2 | module RSpecHelpers 3 | def safely 4 | yield 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/rspec/shared/opal/progress_json_formatter.rb: -------------------------------------------------------------------------------- 1 | module Opal::RSpec 2 | # a good compromise between not mucking with the code we're testing but making it more machine readable 3 | class ProgressJsonFormatter < ::RSpec::Core::Formatters::JsonFormatter 4 | ::RSpec::Core::Formatters.register self, :message, :dump_summary, :dump_profile, :example_passed, :example_pending, :example_failed, :close, :stop 5 | 6 | def example_passed(_notification) 7 | output.print ::RSpec::Core::Formatters::ConsoleCodes.wrap('.', :success) 8 | end 9 | 10 | def example_pending(_notification) 11 | output.print ::RSpec::Core::Formatters::ConsoleCodes.wrap('*', :pending) 12 | end 13 | 14 | def example_failed(_notification) 15 | output.print ::RSpec::Core::Formatters::ConsoleCodes.wrap('F', :failure) 16 | end 17 | 18 | def start_dump(_notification) 19 | output.puts 20 | end 21 | 22 | def close(_notification) 23 | output.puts # our dots will not have closed out with a CR 24 | output.puts 'BEGIN JSON' 25 | super 26 | output.puts # Need a CR for popen to know we're done 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/rspec/shared/opal_filters.rb: -------------------------------------------------------------------------------- 1 | module OpalFilters 2 | extend self 3 | 4 | # class FiltersFormatter < RSpec::Core::Formatters::BaseFormatter 5 | # RSpec::Core::Formatters.register self, :dump_summary 6 | ::RSpec::Core::Notifications::SummaryNotification.class_eval do 7 | def colorized_rerun_commands(colorizer=::RSpec::Core::Formatters::ConsoleCodes) 8 | "\nFilter failed examples:\n\n" + 9 | failed_examples.map do |example| 10 | output = colorizer.wrap("fails #{example.full_description.inspect}, ", RSpec.configuration.failure_color) + " " 11 | output += colorizer.wrap("#{example.execution_result.exception.message.strip.split("\n").first[0..100].inspect}", RSpec.configuration.detail_color) 12 | rescue 13 | # it's ok 14 | ensure 15 | output 16 | end.join("\n") 17 | end 18 | end 19 | 20 | def group(name, &block) 21 | old_name = @name 22 | @name = name 23 | @filters ||= {} 24 | instance_eval(&block) 25 | @name = old_name 26 | end 27 | 28 | FIXME = 'FIXME' 29 | 30 | def fails(full_description, note = nil) 31 | note = "#{name}: #{note || FIXME}" 32 | @filters[full_description] = note || full_description 33 | end 34 | 35 | alias fails_context fails 36 | 37 | def filtered?(full_description) 38 | @filters[full_description] 39 | end 40 | 41 | def pending_message(full_description) 42 | note = @filters[full_description] 43 | "#{@name}: #{note}" 44 | end 45 | end 46 | 47 | module SkipContextSupport 48 | def context(description, *args, &block) 49 | full_description = metadata[:full_description] + ' ' + description 50 | if OpalFilters.filtered?(full_description) 51 | puts "SKIPPING #{full_description}" 52 | else 53 | super 54 | end 55 | end 56 | end 57 | 58 | RSpec.configure do |config| 59 | config.extend(SkipContextSupport) 60 | 61 | config.around(:each) do |example| 62 | desc = example.full_description 63 | pending_message = OpalFilters.pending_message(desc) 64 | 65 | if OpalFilters.filtered?(desc) 66 | pending(pending_message) 67 | else 68 | example.call 69 | 70 | # Hacky hack: some examples don't have description (like it {}) 71 | # and RSpec generates it *after* running the test 72 | # (basically it generates it from the expectation) 73 | desc = example.full_description 74 | pending_message = OpalFilters.pending_message(desc) 75 | if OpalFilters.filtered?(desc) 76 | # Flushing instance variable `@exception` 77 | # allows marking the test as pending after running 78 | # (Note: newer versions of RSpec don't require it) 79 | RSpec.current_example.instance_variable_set(:@exception, nil) 80 | pending("In runtime: #{pending_message}") 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /spec/rspec/support/capybara.rb: -------------------------------------------------------------------------------- 1 | require "capybara/rspec" 2 | require "capybara/apparition" 3 | 4 | Capybara.register_driver :apparition_opal do |app| 5 | Capybara::Apparition::Driver.new(app, 6 | browser_options: { 'no-sandbox' => true } 7 | ) 8 | end 9 | 10 | Capybara.javascript_driver = :apparition_opal 11 | Capybara.default_driver = :apparition_opal 12 | -------------------------------------------------------------------------------- /spec/rspec/support/upstream_tests.rb: -------------------------------------------------------------------------------- 1 | require 'opal-rspec' 2 | 3 | module Opal::RSpec::UpstreamTests 4 | end 5 | 6 | pattern = File.expand_path('../upstream_tests/**/*.rb', __FILE__) 7 | Dir[pattern].each { |f| require f } 8 | -------------------------------------------------------------------------------- /spec/rspec/support/upstream_tests/config.rb: -------------------------------------------------------------------------------- 1 | class Opal::RSpec::UpstreamTests::Config 2 | def initialize(gem_name = ::RSpec.current_example.metadata[:gem_name]) 3 | @gem_name = gem_name 4 | end 5 | 6 | def stubs 7 | [ 8 | 'rubygems', 9 | 'aruba/api', # Cucumber lib that supports file creation during testing, N/A for us 10 | 'simplecov', # hooks aren't available on Opal 11 | 'tmpdir', 12 | 'rspec/support/spec/shell_out', # only does stuff Opal can't support anyways 13 | 'rspec/support/spec/library_wide_checks', # `git ls-files -z` 14 | 'timeout', 15 | 'yaml', 16 | 'support/capybara', 17 | ] 18 | end 19 | 20 | def files_to_run 21 | Opal::RSpec::UpstreamTests::FilesToRun.new(@gem_name).to_a 22 | end 23 | 24 | def load_paths 25 | [ 26 | File.join(submodule_root, 'spec'), 27 | 'spec/rspec/shared' 28 | ] 29 | end 30 | 31 | def submodule_root 32 | File.expand_path("../../../../../#{@gem_name}", __FILE__) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/rspec/support/upstream_tests/files_to_run.rb: -------------------------------------------------------------------------------- 1 | class Opal::RSpec::UpstreamTests::FilesToRun 2 | def initialize(gem_name = ::RSpec.current_example.metadata[:gem_name]) 3 | @gem_name = gem_name 4 | end 5 | 6 | def to_a 7 | FileList[include_pattern].exclude(exclude_pattern) 8 | end 9 | 10 | private 11 | 12 | def submodule_root 13 | File.expand_path("../../../../../#{@gem_name}", __FILE__) 14 | end 15 | 16 | def gem_root 17 | File.join(submodule_root, 'upstream') 18 | end 19 | 20 | def include_pattern 21 | File.join(gem_root, 'spec/**/*_spec.rb') 22 | end 23 | 24 | def exclude_pattern 25 | filepath = File.join(submodule_root, 'spec/files_to_exclude.txt') 26 | content = File.read(filepath) 27 | patterns = content.split("\n").reject { |line| line.empty? || line.start_with?('#') } 28 | 29 | missing_exclusions = patterns.select do |pattern| 30 | FileList[pattern].empty? 31 | end 32 | if missing_exclusions.any? 33 | raise "Expected to exclude #{missing_exclusions} as noted in spec_files_exclude.txt but we didn't find these files. Has RSpec been upgraded?" 34 | end 35 | 36 | patterns 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/rspec/support/upstream_tests/result.rb: -------------------------------------------------------------------------------- 1 | Opal::RSpec::UpstreamTests::Result = Struct.new(:exit_status, :output, :json) do 2 | def quoted_output 3 | "> "+output.gsub(/(\n)/, '\1> ') 4 | end 5 | 6 | def successful? 7 | exit_status == 0 8 | end 9 | 10 | def inspect 11 | "#" 12 | end 13 | 14 | alias to_s inspect 15 | end 16 | -------------------------------------------------------------------------------- /spec/rspec/support/upstream_tests/runner.rb: -------------------------------------------------------------------------------- 1 | class Opal::RSpec::UpstreamTests::Runner 2 | def initialize(gem_name = ::RSpec.current_example.metadata[:gem_name]) 3 | @gem_name = gem_name 4 | @config = Opal::RSpec::UpstreamTests::Config.new(gem_name) 5 | end 6 | 7 | def run 8 | @config.stubs.each { |f| ::Opal::Config.stubbed_files << f } 9 | 10 | if ENV['OPAL_RAW_RUN'] 11 | opal_rspec_runner.run 12 | else 13 | output, exit_status = StdoutCapturingRunner.run { opal_rspec_runner.run } 14 | end 15 | 16 | Opal::RSpec::UpstreamTests::Result.new( 17 | exit_status, 18 | output, 19 | JSON.parse(File.read("/tmp/#{@gem_name}-results.json"), symbolize_names: true), 20 | ) 21 | end 22 | 23 | module StdoutCapturingRunner 24 | def self.run 25 | output_io = StringIO.new 26 | previous_stdout = $stdout 27 | previous_stderr = $stderr 28 | $stdout = output_io 29 | $stderr = output_io 30 | begin 31 | exit_status = yield 32 | ensure 33 | $stdout = previous_stdout 34 | $stderr = previous_stderr 35 | end 36 | output_io.rewind 37 | [output_io.read, exit_status] 38 | end 39 | end 40 | 41 | private 42 | 43 | def opal_rspec_runner 44 | ::Opal::RSpec::Runner.new do |server, task| 45 | # A lot of specs, can take longer on slower machines 46 | # task.timeout = 80000 47 | 48 | task.files = @config.files_to_run 49 | task.default_path = "#{@gem_name}/upstream/spec" 50 | 51 | @config.load_paths.each do |path| 52 | server.append_path(path) 53 | end 54 | 55 | server.debug = ENV['OPAL_DEBUG'] 56 | task.requires.unshift(File.join(@config.submodule_root, 'spec/requires.rb')) 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause this 4 | # file to always be loaded, without a need to explicitly require it in any files. 5 | # 6 | # Given that it is always loaded, you are encouraged to keep this file as 7 | # light-weight as possible. Requiring heavyweight dependencies from this file 8 | # will add to the boot time of your test suite on EVERY test run, even for an 9 | # individual file that may not need all of that loaded. Instead, consider making 10 | # a separate helper file that requires the additional dependencies and performs 11 | # the additional setup, and require it from the spec files that actually need it. 12 | # 13 | # The `.rspec` file also contains a few flags that are not defaults but that 14 | # users commonly want. 15 | # 16 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 17 | RSpec.configure do |config| 18 | # rspec-expectations config goes here. You can use an alternate 19 | # assertion/expectation library such as wrong or the stdlib/minitest 20 | # assertions if you prefer. 21 | config.expect_with :rspec do |expectations| 22 | # This option will default to `true` in RSpec 4. It makes the `description` 23 | # and `failure_message` of custom matchers include text for helper methods 24 | # defined using `chain`, e.g.: 25 | # be_bigger_than(2).and_smaller_than(4).description 26 | # # => "be bigger than 2 and smaller than 4" 27 | # ...rather than: 28 | # # => "be bigger than 2" 29 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 30 | end 31 | 32 | # rspec-mocks config goes here. You can use an alternate test double 33 | # library (such as bogus or mocha) by changing the `mock_with` option here. 34 | config.mock_with :rspec do |mocks| 35 | # Prevents you from mocking or stubbing a method that does not exist on 36 | # a real object. This is generally recommended, and will default to 37 | # `true` in RSpec 4. 38 | mocks.verify_partial_doubles = true 39 | end 40 | 41 | # These two settings work together to allow you to limit a spec run 42 | # to individual examples or groups you care about by tagging them with 43 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples 44 | # get run. 45 | config.filter_run :focus 46 | config.run_all_when_everything_filtered = true 47 | 48 | # Limits the available syntax to the non-monkey patched syntax that is recommended. 49 | # For more details, see: 50 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 51 | # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 52 | # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching 53 | config.disable_monkey_patching! 54 | 55 | # This setting enables warnings. It's recommended, but in some cases may 56 | # be too noisy due to issues in dependencies. 57 | # config.warnings = true 58 | 59 | # Many RSpec users commonly either run the entire suite or an individual 60 | # file, and it's useful to allow more verbose output when running an 61 | # individual spec file. 62 | if config.files_to_run.one? 63 | # Use the documentation formatter for detailed output, 64 | # unless a formatter has already been configured 65 | # (e.g. via a command-line flag). 66 | config.default_formatter = 'doc' 67 | end 68 | 69 | # Print the 10 slowest examples and example groups at the 70 | # end of the spec run, to help surface which specs are running 71 | # particularly slow. 72 | config.profile_examples = 10 73 | 74 | # Run specs in random order to surface order dependencies. If you find an 75 | # order dependency and want to debug it, you can fix the order by providing 76 | # the seed, which is printed after each run. 77 | # --seed 1234 78 | config.order = :random 79 | 80 | # Seed global randomization in this process using the `--seed` CLI option. 81 | # Setting this allows you to use `--seed` to deterministically reproduce 82 | # test failures related to randomization by passing the same `--seed` value 83 | # as the one that triggered the failure. 84 | Kernel.srand config.seed 85 | end 86 | 87 | 88 | 89 | 90 | require 'rspec' 91 | require 'rspec/support/capybara' 92 | require 'rspec/support/upstream_tests' 93 | require 'opal' 94 | require 'opal/rspec' 95 | Opal.raise_on_deprecation = true 96 | SPEC_ROOT = __dir__ 97 | -------------------------------------------------------------------------------- /stubs/cgi/escape.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/cgi/escape.rb -------------------------------------------------------------------------------- /stubs/cgi/util.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/cgi/util.rb -------------------------------------------------------------------------------- /stubs/coderay.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/coderay.rb -------------------------------------------------------------------------------- /stubs/drb/acl.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/drb/acl.rb -------------------------------------------------------------------------------- /stubs/drb/drb.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/drb/drb.rb -------------------------------------------------------------------------------- /stubs/erb/version.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/erb/version.rb -------------------------------------------------------------------------------- /stubs/minitest.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/minitest.rb -------------------------------------------------------------------------------- /stubs/minitest/assertions.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/minitest/assertions.rb -------------------------------------------------------------------------------- /stubs/minitest/unit.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/minitest/unit.rb -------------------------------------------------------------------------------- /stubs/mutex_m.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/mutex_m.rb -------------------------------------------------------------------------------- /stubs/open3.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/open3.rb -------------------------------------------------------------------------------- /stubs/psych.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/psych.rb -------------------------------------------------------------------------------- /stubs/ripper.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/ripper.rb -------------------------------------------------------------------------------- /stubs/rubygems/bundler_version_finder.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/rubygems/bundler_version_finder.rb -------------------------------------------------------------------------------- /stubs/rubygems/util.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/rubygems/util.rb -------------------------------------------------------------------------------- /stubs/socket.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/socket.rb -------------------------------------------------------------------------------- /stubs/tempfile.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/tempfile.rb -------------------------------------------------------------------------------- /stubs/test/unit/assertions.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/test/unit/assertions.rb -------------------------------------------------------------------------------- /stubs/thread_order.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opal/opal-rspec/8acd492c077617632b5bbbb1f1290e3647b1a8a0/stubs/thread_order.rb -------------------------------------------------------------------------------- /tasks/building.rake: -------------------------------------------------------------------------------- 1 | Bundler::GemHelper.install_tasks 2 | 3 | desc 'Generates an RSpec requires file free of dynamic requires' 4 | task :generate_requires do 5 | # Do this free of any requires used to make this Rake task happen 6 | sh 'bin/generate_requires' 7 | end 8 | 9 | desc "Build build/opal-rspec.js" 10 | task :dist do 11 | require 'fileutils' 12 | FileUtils.mkdir_p 'build' 13 | 14 | builder = Opal::Builder.new(stubs: Opal::Config.stubbed_files, # stubs already specified in lib/opal/rspec.rb 15 | compiler_options: { dynamic_require_severity: :ignore }) # RSpec is full of dynamic requires 16 | src = builder.build('opal-rspec') 17 | 18 | min = uglify src 19 | gzp = gzip min 20 | 21 | File.open('build/opal-rspec.js', 'w+') do |out| 22 | out << src 23 | end 24 | 25 | puts "development: #{src.size}, minified: #{min.size}, gzipped: #{gzp.size}" 26 | end 27 | 28 | # Used for uglifying source to minify 29 | def uglify(str) 30 | IO.popen('uglifyjs', 'r+') do |i| 31 | i.puts str 32 | i.close_write 33 | return i.read 34 | end 35 | rescue Errno::ENOENT 36 | $stderr.puts '"uglifyjs" command not found (install with: "npm install -g uglify-js")' 37 | nil 38 | end 39 | 40 | # Gzip code to check file size 41 | def gzip(str) 42 | IO.popen('gzip -f', 'r+') do |i| 43 | i.puts str 44 | i.close_write 45 | return i.read 46 | end 47 | rescue Errno::ENOENT 48 | $stderr.puts '"gzip" command not found, it is required to produce the .gz version' 49 | nil 50 | end 51 | -------------------------------------------------------------------------------- /tasks/testing.rake: -------------------------------------------------------------------------------- 1 | require 'rspec/core/rake_task' 2 | 3 | # Rake.application.last_comment was removed in Rake v12.0 4 | # https://github.com/ruby/rake/commit/e76242ce7ef94568399a50b69bda4b723dab7c75 5 | def (Rake.application).last_comment; last_description; end unless Rake.application.respond_to? :last_comment 6 | 7 | desc 'Runs all RSpec specs' 8 | task :rspec do 9 | sh 'bundle exec rspec' 10 | end 11 | 12 | require 'opal/rspec/rake_task' 13 | 14 | desc 'Runs a set of specs in opal' 15 | Opal::RSpec::RakeTask.new do |_, task| 16 | task.pattern = ENV['PATTERN'] || 'spec-opal/**/*_spec.{rb,opal}' 17 | end 18 | 19 | # 20 | # desc 'Run the full suite, this can time out on Travis' 21 | # task :default => [ 22 | # :unit_specs, 23 | # :verify_opal_specs, 24 | # :integration_specs, 25 | # :verify_rspec_specs, 26 | # ] 27 | # 28 | # desc 'Run only tests that use the opal-rspec Rake task' 29 | # task :rake_only => [ 30 | # :verify_rspec_specs, 31 | # :verify_opal_specs, 32 | # ] 33 | # 34 | # desc 'Sanity checks a given version of MRI and run a basic check' 35 | # task :mri_sanity_check => [ 36 | # :unit_specs, 37 | # :integration_specs, 38 | # ] 39 | # 40 | # 41 | # desc 'Runs a test to test browser based specs using Opal specs in spec-opal' 42 | # RSpec::Core::RakeTask.new :integration_specs do |t| 43 | # t.pattern = 'spec/integration/**/*_spec.rb' 44 | # end 45 | # 46 | # desc 'Unit tests for MRI focused components of opal-rspec' 47 | # RSpec::Core::RakeTask.new :unit_specs do |t| 48 | # t.pattern = 'spec/opal/rspec/**/*_spec.rb' 49 | # end 50 | # 51 | Opal::RSpec::RakeTask.new(:other_specs) do |_, task| 52 | task.pattern = 'spec-opal/other/dummy_spec.rb' 53 | end 54 | 55 | Opal::RSpec::RakeTask.new(:color_on_by_default) do |_, task| 56 | task.pattern = 'spec-opal/other/color_on_by_default_spec.rb' 57 | end 58 | # 59 | # # Opal::RSpec::CoreSpecLoader.rake_tasks_for(:rspec_core_specs) 60 | # # Opal::RSpec::ExpectationSpecLoader.rake_tasks_for(:rspec_expectation_specs) 61 | # # Opal::RSpec::SupportSpecLoader.rake_tasks_for(:rspec_support_specs) 62 | # # Opal::RSpec::MocksSpecLoader.rake_tasks_for(:rspec_mocks_specs) 63 | # 64 | # # These are done 65 | # desc 'Verifies all RSpec specs' 66 | # task :verify_rspec_specs => [ 67 | # :verify_rspec_support_specs, 68 | # :verify_rspec_core_specs, 69 | # :verify_rspec_expectation_specs, 70 | # :verify_rspec_mocks_specs, 71 | # ] 72 | # 73 | # desc 'Will run a spec suite (rake spec:opal) and check for expected combination of failures and successes' 74 | # task :verify_opal_specs do 75 | # sh 'rspec spec/integration/verify_opal_specs_spec.rb' 76 | # end 77 | # 78 | --------------------------------------------------------------------------------