hyper-spec 1.0.alpha1.8 → 1.0.0.lap28

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -5,6 +5,8 @@ RSpec::Core::RakeTask.new(:spec)
5
5
 
6
6
  namespace :spec do
7
7
  task :prepare do
8
+ sh %{bundle update}
9
+ sh %{cd spec/test_app; bundle update}
8
10
  end
9
11
  end
10
12
 
data/bin/console CHANGED
File without changes
data/bin/setup CHANGED
File without changes
data/dciy.toml ADDED
@@ -0,0 +1,3 @@
1
+ [dciy.commands]
2
+ prepare = ["rake spec:prepare"]
3
+ cibuild = ["bundle exec rake"]
data/hyper-spec.gemspec CHANGED
@@ -6,51 +6,53 @@ require 'hyper-spec/version'
6
6
  Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
7
7
  spec.name = 'hyper-spec'
8
8
  spec.version = HyperSpec::VERSION
9
- spec.authors = ['Mitch VanDuyn', 'AdamCreekroad', 'Jan Biedermann']
9
+ spec.authors = ['Mitch VanDuyn', 'Adam Creekroad', 'Jan Biedermann']
10
10
  spec.email = ['mitch@catprint.com', 'jan@kursator.com']
11
- spec.summary = 'Drive your Opal and Hyperstack client and server specs from RSpec and Capybara'
12
- spec.description = 'A Hyperstack application consists of isomorphic React Components, '\
11
+ spec.summary = 'Drive your Hyperloop client and server specs from RSpec and Capybara'
12
+ spec.description = 'A Hyperloop application consists of isomorphic React Components, '\
13
13
  'Active Record Models, Stores, Operations and Policiespec. '\
14
14
  'Test them all from Rspec, regardless if the code runs on the client or server.'
15
- spec.homepage = 'http://hyperstack.org'
15
+ spec.homepage = 'http://ruby-hyperloop.org'
16
16
  spec.license = 'MIT'
17
+ # spec.metadata = {
18
+ # "homepage_uri" => 'http://ruby-hyperloop.org',
19
+ # "source_code_uri" => 'https://github.com/ruby-hyperloop/hyper-component'
20
+ # }
21
+
17
22
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(gemfiles|spec)/}) }
18
23
  spec.bindir = 'exe'
19
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
25
  spec.require_paths = ['lib']
21
26
 
22
- spec.add_dependency 'actionview'
23
27
  spec.add_dependency 'capybara'
24
- spec.add_dependency 'chromedriver-helper', '1.2.0'
25
- spec.add_dependency 'filecache'
28
+ spec.add_dependency 'chromedriver-helper'
26
29
  spec.add_dependency 'method_source'
27
- spec.add_dependency 'opal', ENV['OPAL_VERSION'] || '>= 0.11.0', '< 2.0'
28
- spec.add_dependency 'parser'
29
- spec.add_dependency 'rspec'
30
+ spec.add_dependency 'opal', '>= 0.11.0', '< 0.12.0'
31
+ spec.add_dependency 'parser', '>= 2.3.3.1'
32
+ spec.add_dependency 'pry'
33
+ spec.add_dependency 'rspec-rails'
30
34
  spec.add_dependency 'selenium-webdriver'
31
35
  spec.add_dependency 'timecop', '~> 0.8.1'
32
36
  spec.add_dependency 'uglifier'
33
- spec.add_dependency 'unparser', '>= 0.4.2'
37
+ spec.add_dependency 'unparser'
34
38
  spec.add_dependency 'webdrivers'
35
39
 
36
40
  spec.add_development_dependency 'bundler'
37
- spec.add_development_dependency 'hyper-component', HyperSpec::VERSION
38
- spec.add_development_dependency 'mini_racer'
41
+ spec.add_development_dependency 'hyper-react', HyperSpec::VERSION
42
+ spec.add_development_dependency 'mini_racer', '~> 0.1.15'
39
43
  spec.add_development_dependency 'opal-browser', '~> 0.2.0'
40
- spec.add_development_dependency 'opal-rails', '>= 0.9.4'
41
- spec.add_development_dependency 'pry-rescue'
42
- spec.add_development_dependency 'pry-stack_explorer'
44
+ spec.add_development_dependency 'opal-rails', '~> 0.9.4'
43
45
  spec.add_development_dependency 'puma'
44
- spec.add_development_dependency 'rails', ENV['RAILS_VERSION'] || '>= 5.0.0', '< 7.0'
46
+ spec.add_development_dependency 'rails', '>= 4.0.0'
45
47
  spec.add_development_dependency 'rake'
48
+ spec.add_development_dependency 'pry-rescue'
46
49
  spec.add_development_dependency 'react-rails', '>= 2.3.0', '< 2.5.0'
47
- spec.add_development_dependency 'rspec-rails'
48
50
  spec.add_development_dependency 'rspec-collection_matchers'
49
51
  spec.add_development_dependency 'rspec-expectations'
50
52
  spec.add_development_dependency 'rspec-its'
51
53
  spec.add_development_dependency 'rspec-mocks'
52
54
  spec.add_development_dependency 'rspec-steps', '~> 2.1.1'
53
- spec.add_development_dependency 'rubocop' #, '~> 0.51.0'
55
+ spec.add_development_dependency 'rubocop', '~> 0.51.0'
54
56
  spec.add_development_dependency 'shoulda'
55
57
  spec.add_development_dependency 'shoulda-matchers'
56
58
  spec.add_development_dependency 'spring-commands-rspec'
data/lib/hyper-spec.rb CHANGED
@@ -1,144 +1,14 @@
1
- # hyper-spec
2
- require 'action_view'
3
- require 'opal'
4
- require 'unparser'
5
- require 'method_source'
6
- require 'filecache'
7
-
8
1
  require 'capybara/rspec'
9
- require 'hyper-spec/internal/client_execution'
10
- require 'hyper-spec/internal/component_mount'
11
- require 'hyper-spec/internal/controller'
12
- require 'hyper-spec/internal/copy_locals'
13
- require 'hyper-spec/internal/patches'
14
- require 'hyper-spec/internal/rails_controller_helpers'
15
- require 'hyper-spec/internal/time_cop.rb'
16
- require 'hyper-spec/internal/window_sizing'
17
-
18
- require 'hyper-spec/controller_helpers'
19
-
20
- require 'hyper-spec/wait_for_ajax'
21
-
22
- require 'hyper-spec/helpers'
23
- require 'hyper-spec/expectations'
24
-
25
- require 'parser/current'
26
- if defined?(Selenium::WebDriver::Firefox)
27
- require 'selenium/web_driver/firefox/profile'
28
- end
2
+ require 'opal'
29
3
  require 'selenium-webdriver'
30
4
 
5
+ require 'hyper-spec/component_test_helpers'
31
6
  require 'hyper-spec/version'
32
-
33
-
34
- begin
35
- require 'pry'
36
- rescue LoadError
37
- nil
38
- end
39
-
40
- # opt-in to most recent AST format:
41
- Parser::Builders::Default.emit_lambda = true
42
- Parser::Builders::Default.emit_procarg0 = true
43
- (Parser::Builders::Default.emit_encoding = true) rescue nil
44
- (Parser::Builders::Default.emit_index = true) rescue nil
45
- (Parser::Builders::Default.emit_arg_inside_procarg0 = true) rescue nil
46
- (Parser::Builders::Default.emit_forward_arg = true) rescue nil
47
- (Parser::Builders::Default.emit_kwargs = true) rescue nil
48
- (Parser::Builders::Default.emit_match_pattern = true) rescue nil
49
-
50
- # not available in parser 2.3
51
- if Parser::Builders::Default.respond_to? :emit_arg_inside_procarg0
52
- Parser::Builders::Default.emit_arg_inside_procarg0 = true
53
- end
54
-
55
- module HyperSpec
56
- if defined? Pry
57
- # add a before eval hook to pry so we can capture the source
58
- class << self
59
- attr_accessor :current_pry_code_block
60
- Pry.hooks.add_hook(:before_eval, 'hyper_spec_code_capture') do |code|
61
- HyperSpec.current_pry_code_block = code
62
- end
63
- end
64
- end
65
-
66
- def self.reset_between_examples
67
- @reset_between_examples ||= []
68
- end
69
-
70
- def self.reset_between_examples?
71
- RSpec.configuration.reset_between_examples
72
- end
73
-
74
- def self.reset_sessions!
75
- Capybara.old_reset_sessions!
76
- end
77
- end
78
-
79
- # TODO: figure out why we need this patch - its because we are on an old version
80
- # of Selenium Webdriver, but why?
81
- require 'selenium-webdriver'
82
-
83
- module Selenium
84
- module WebDriver
85
- module Chrome
86
- module Bridge
87
- COMMANDS = remove_const(:COMMANDS).dup
88
- COMMANDS[:get_log] = [:post, 'session/:session_id/log']
89
- COMMANDS.freeze
90
-
91
- def log(type)
92
- data = execute :get_log, {}, type: type.to_s
93
-
94
- Array(data).map do |l|
95
- begin
96
- LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
97
- rescue KeyError
98
- next
99
- end
100
- end
101
- end
102
- end
103
- end
104
- end
105
- end
106
-
107
- module Capybara
108
- class << self
109
- alias old_reset_sessions! reset_sessions!
110
- def reset_sessions!
111
- old_reset_sessions! if HyperSpec.reset_between_examples?
112
- end
113
- end
114
- end
115
-
116
- RSpec.configure do |config|
117
- config.add_setting :reset_between_examples, default: true
118
- config.before(:all, no_reset: true) do
119
- HyperSpec.reset_between_examples << RSpec.configuration.reset_between_examples
120
- RSpec.configuration.reset_between_examples = false
121
- end
122
- config.before(:all, no_reset: false) do
123
- HyperSpec.reset_between_examples << RSpec.configuration.reset_between_examples
124
- RSpec.configuration.reset_between_examples = true
125
- end
126
- config.after(:all) do
127
- HyperSpec.reset_sessions! unless HyperSpec.reset_between_examples?
128
- # If rspecs step is used first in a file, it will NOT call config.before(:all) causing the
129
- # reset_between_examples stack to be mismatched, so we check, if its already empty we
130
- # just leave.
131
- next if HyperSpec.reset_between_examples.empty?
132
-
133
- RSpec.configuration.reset_between_examples = HyperSpec.reset_between_examples.pop
134
- end
135
- config.before(:each) do |example|
136
- insure_page_loaded(true) if example.metadata[:js] && !HyperSpec.reset_between_examples?
137
- end
138
- end
7
+ require 'hyper-spec/wait_for_ajax'
8
+ require 'selenium/web_driver/firefox/profile'
139
9
 
140
10
  RSpec.configure do |config|
141
- config.include HyperSpec::Helpers
11
+ config.include HyperSpec::ComponentTestHelpers
142
12
  config.include HyperSpec::WaitForAjax
143
13
  config.include Capybara::DSL
144
14
 
@@ -147,21 +17,17 @@ RSpec.configure do |config|
147
17
  config.add_setting :debugger_width, default: nil
148
18
 
149
19
  config.before(:each) do
150
- if defined?(Hyperstack)
151
- Hyperstack.class_eval do
152
- def self.on_server?
153
- true
154
- end
20
+ Hyperloop.class_eval do
21
+ def self.on_server?
22
+ true
155
23
  end
156
- end
24
+ end if defined?(Hyperloop)
157
25
  # for compatibility with HyperMesh
158
- if defined?(HyperMesh)
159
- HyperMesh.class_eval do
160
- def self.on_server?
161
- true
162
- end
26
+ HyperMesh.class_eval do
27
+ def self.on_server?
28
+ true
163
29
  end
164
- end
30
+ end if defined?(HyperMesh)
165
31
  end
166
32
 
167
33
  config.before(:each, js: true) do
@@ -177,30 +43,22 @@ RSpec.configure do |config|
177
43
  PusherFake::Channel.reset if defined? PusherFake
178
44
  end
179
45
  end
46
+
180
47
  end
181
48
 
182
49
  # Capybara config
183
- RSpec.configure do |config|
184
- config.before(:each) do |example|
185
- HyperSpec::Internal::Controller.current_example = example
186
- HyperSpec::Internal::Controller.description_displayed = false
187
- end
188
-
189
- config.add_setting :wait_for_initialization_time
190
- config.wait_for_initialization_time = 3
191
-
50
+ RSpec.configure do |_config|
192
51
  Capybara.default_max_wait_time = 10
193
52
 
194
53
  Capybara.register_driver :chrome do |app|
195
54
  options = {}
196
55
  options.merge!(
197
- w3c: false,
198
- args: %w[auto-open-devtools-for-tabs]
199
- )
56
+ args: %w[auto-open-devtools-for-tabs],
57
+ prefs: { 'devtools.open_docked' => false, "devtools.currentDockState" => "undocked", devtools: {currentDockState: :undocked} }
58
+ ) unless ENV['NO_DEBUGGER']
59
+ # this does not seem to work properly. Don't document this feature yet.
200
60
  options['mobileEmulation'] = { 'deviceName' => ENV['DEVICE'].tr('-', ' ') } if ENV['DEVICE']
201
- capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
202
- chromeOptions: options, 'goog:loggingPrefs' => { browser: 'ALL' }
203
- )
61
+ capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(chromeOptions: options)
204
62
  Capybara::Selenium::Driver.new(app, browser: :chrome, desired_capabilities: capabilities)
205
63
  end
206
64
 
@@ -208,20 +66,11 @@ RSpec.configure do |config|
208
66
  Capybara::Selenium::Driver.new(app, browser: :firefox)
209
67
  end
210
68
 
211
- Capybara.register_driver :chrome_headless_docker_travis do |app|
212
- options = ::Selenium::WebDriver::Chrome::Options.new
213
- options.add_argument('--headless')
214
- options.add_argument('--no-sandbox')
215
- options.add_argument('--disable-dev-shm-usage')
216
- Selenium::WebDriver::Chrome::Service.driver_path = '/usr/lib/chromium-browser/chromedriver'
217
- Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
218
- end
219
-
220
69
  Capybara.register_driver :firefox_headless do |app|
221
70
  options = Selenium::WebDriver::Firefox::Options.new
222
71
  options.headless!
223
72
  Capybara::Selenium::Driver.new(app, browser: :firefox, options: options)
224
- end if defined?(Selenium::WebDriver::Firefox)
73
+ end
225
74
 
226
75
  Capybara.register_driver :selenium_with_firebug do |app|
227
76
  profile = Selenium::WebDriver::Firefox::Profile.new
@@ -229,7 +78,7 @@ RSpec.configure do |config|
229
78
  profile.enable_firebug
230
79
  options = Selenium::WebDriver::Firefox::Options.new(profile: profile)
231
80
  Capybara::Selenium::Driver.new(app, browser: :firefox, options: options)
232
- end if defined?(Selenium::WebDriver::Firefox)
81
+ end
233
82
 
234
83
  Capybara.register_driver :safari do |app|
235
84
  Capybara::Selenium::Driver.new(app, browser: :safari)
@@ -243,7 +92,7 @@ RSpec.configure do |config|
243
92
  when 'firefox' then :firefox
244
93
  when 'headless' then :selenium_chrome_headless
245
94
  when 'safari' then :safari
246
- when 'travis' then :chrome_headless_docker_travis
247
95
  else :selenium_chrome_headless
248
96
  end
97
+
249
98
  end
@@ -0,0 +1,360 @@
1
+ # see component_test_helpers_spec.rb for examples
2
+ require 'parser/current'
3
+ require 'unparser'
4
+ require 'method_source'
5
+ require_relative '../../lib/hyper-spec/time_cop.rb'
6
+
7
+ module HyperSpec
8
+ module ComponentTestHelpers
9
+ TOP_LEVEL_COMPONENT_PATCH =
10
+ Opal.compile(File.read(File.expand_path('../../react/top_level_rails_component.rb', __FILE__)))
11
+ TIME_COP_CLIENT_PATCH =
12
+ Opal.compile(File.read(File.expand_path('../../hyper-spec/time_cop.rb', __FILE__))) +
13
+ "\n#{File.read(File.expand_path('../../sources/lolex.js', __FILE__))}"
14
+
15
+ class << self
16
+ attr_accessor :current_example
17
+ attr_accessor :description_displayed
18
+
19
+ def display_example_description
20
+ "<script type='text/javascript'>console.log('%c#{current_example.description}'"\
21
+ ",'color:green; font-weight:bold; font-size: 200%')</script>"
22
+ end
23
+ end
24
+
25
+ def build_test_url_for(controller)
26
+ unless controller
27
+ unless defined?(::ReactTestController)
28
+ Object.const_set('ReactTestController', Class.new(::ActionController::Base))
29
+ end
30
+
31
+ controller = ::ReactTestController
32
+ end
33
+
34
+ route_root = controller.name.gsub(/Controller$/, '').underscore
35
+
36
+ unless controller.method_defined?(:test)
37
+ controller.class_eval do
38
+ define_method(:test) do
39
+ route_root = self.class.name.gsub(/Controller$/, '').underscore
40
+ test_params = ::Rails.cache.read("/#{route_root}/#{params[:id]}")
41
+ @component_name = test_params[0]
42
+ @component_params = test_params[1]
43
+ render_params = test_params[2]
44
+ render_on = render_params.delete(:render_on) || :client_only
45
+ _mock_time = render_params.delete(:mock_time)
46
+ style_sheet = render_params.delete(:style_sheet)
47
+ javascript = render_params.delete(:javascript)
48
+ code = render_params.delete(:code)
49
+
50
+ page = '<%= react_component @component_name, @component_params, '\
51
+ "{ prerender: #{render_on != :client_only} } %>"
52
+ unless render_on == :server_only
53
+ page = "<script type='text/javascript'>\n#{TOP_LEVEL_COMPONENT_PATCH}\n</script>\n#{page}"
54
+ page = "<script type='text/javascript'>\n#{code}\n</script>\n#{page}" if code
55
+ end
56
+
57
+ if render_on != :server_only || Lolex.initialized?
58
+ page = "<script type='text/javascript'>\n#{TIME_COP_CLIENT_PATCH}\n</script>\n#{page}"
59
+ end
60
+
61
+ if (render_on != :server_only && !render_params[:layout]) || javascript
62
+ page = "<%= javascript_include_tag '#{javascript || 'application'}' %>\n#{page}"
63
+ end
64
+
65
+ if !render_params[:layout] || style_sheet
66
+ page = "<%= stylesheet_link_tag '#{style_sheet || 'application'}' %>\n#{page}"
67
+ end
68
+ page = "<script type='text/javascript'>go = function() "\
69
+ "{window.hyper_spec_waiting_for_go = false}</script>\n#{page}"
70
+
71
+ title = view_context.escape_javascript(ComponentTestHelpers.current_example.description)
72
+ title = "#{title}...continued." if ComponentTestHelpers.description_displayed
73
+
74
+ page = "<script type='text/javascript'>console.log('%c#{title}',"\
75
+ "'color:green; font-weight:bold; font-size: 200%')</script>\n#{page}"
76
+
77
+ ComponentTestHelpers.description_displayed = true
78
+ render_params[:inline] = page
79
+ render render_params
80
+ end
81
+ end
82
+
83
+ begin
84
+ routes = ::Rails.application.routes
85
+ routes.disable_clear_and_finalize = true
86
+ routes.clear!
87
+ routes.draw do
88
+ get "/#{route_root}/:id", to: "#{route_root}#test"
89
+ end
90
+ ::Rails.application.routes_reloader.paths.each { |path| load(path) }
91
+ routes.finalize!
92
+ ActiveSupport.on_load(:action_controller) { routes.finalize! }
93
+ ensure
94
+ routes.disable_clear_and_finalize = false
95
+ end
96
+ end
97
+
98
+ "/#{route_root}/#{@test_id = (@test_id || 0) + 1}"
99
+ end
100
+
101
+ def isomorphic(&block)
102
+ yield
103
+ on_client(&block)
104
+ end
105
+
106
+ def evaluate_ruby(str = '', opts = {}, &block)
107
+ insure_mount
108
+ if block
109
+ str = "#{str}\n#{Unparser.unparse Parser::CurrentRuby.parse(block.source).children.last}"
110
+ end
111
+ js = Opal.compile(str).delete("\n").gsub('(Opal);', '(Opal)')
112
+ # workaround for firefox 58 and geckodriver 0.19.1, because firefox is unable to find .$to_json:
113
+ # JSON.parse(evaluate_script("(function(){var a=Opal.Array.$new(); a[0]=#{js}; return a.$to_json();})();"), opts).first
114
+ JSON.parse(evaluate_script("[#{js}].$to_json()"), opts).first
115
+ end
116
+
117
+ def expect_evaluate_ruby(str = '', opts = {}, &block)
118
+ insure_mount
119
+ expect(evaluate_ruby(add_opal_block(str, block), opts))
120
+ end
121
+
122
+ def add_opal_block(str, block)
123
+ # big assumption here is that we are going to follow this with a .to
124
+ # hence .children.first followed by .children.last
125
+ # probably should do some kind of "search" to make this work nicely
126
+ return str unless block
127
+ "#{str}\n"\
128
+ "#{Unparser.unparse Parser::CurrentRuby.parse(block.source).children.first.children.last}"
129
+ end
130
+
131
+ def evaluate_promise(str = '', opts = {}, &block)
132
+ insure_mount
133
+ str = "#{str}\n#{Unparser.unparse Parser::CurrentRuby.parse(block.source).children.last}" if block
134
+ str = "#{str}.then { |args| args = [args]; `window.hyper_spec_promise_result = args` }"
135
+ js = Opal.compile(str).gsub("\n","").gsub("(Opal);","(Opal)")
136
+ page.evaluate_script("window.hyper_spec_promise_result = false")
137
+ page.execute_script(js)
138
+ Timeout.timeout(Capybara.default_max_wait_time) do
139
+ loop do
140
+ sleep 0.25
141
+ break if page.evaluate_script("!!window.hyper_spec_promise_result")
142
+ end
143
+ end
144
+ JSON.parse(page.evaluate_script("window.hyper_spec_promise_result.$to_json()"), opts).first
145
+ end
146
+
147
+ def expect_promise(str = '', opts = {}, &block)
148
+ insure_mount
149
+ expect(evaluate_promise(add_opal_block(str, block), opts))
150
+ end
151
+
152
+ def ppr(str)
153
+ js = Opal.compile(str).delete("\n").gsub('(Opal);', '(Opal)')
154
+ execute_script("console.log(#{js})")
155
+ end
156
+
157
+ def on_client(&block)
158
+ @client_code =
159
+ "#{@client_code}#{Unparser.unparse Parser::CurrentRuby.parse(block.source).children.last}\n"
160
+ end
161
+
162
+ def debugger
163
+ `debugger`
164
+ nil
165
+ end
166
+
167
+ def insure_mount
168
+ # rescue in case page is not defined...
169
+ mount unless page.instance_variable_get('@hyper_spec_mounted')
170
+ end
171
+
172
+ def client_option(opts = {})
173
+ @client_options ||= {}
174
+ @client_options.merge! opts
175
+ end
176
+
177
+ alias client_options client_option
178
+
179
+ def mount(component_name = nil, params = nil, opts = {}, &block)
180
+ unless params
181
+ params = opts
182
+ opts = {}
183
+ end
184
+
185
+ opts = client_options opts
186
+ test_url = build_test_url_for(opts.delete(:controller))
187
+
188
+ if block || @client_code || component_name.nil?
189
+ block_with_helpers = <<-code
190
+ module ComponentHelpers
191
+ def self.js_eval(s)
192
+ `eval(s)`
193
+ end
194
+ def self.dasherize(s)
195
+ res = %x{
196
+ s.replace(/[-_\\s]+/g, '-')
197
+ .replace(/([A-Z\\d]+)([A-Z][a-z])/g, '$1-$2')
198
+ .replace(/([a-z\\d])([A-Z])/g, '$1-$2')
199
+ .toLowerCase()
200
+ }
201
+ res
202
+ end
203
+ def self.add_class(class_name, styles={})
204
+ style = styles.collect { |attr, value| "\#{dasherize(attr)}:\#{value}" }.join("; ")
205
+ cs = class_name.to_s
206
+ %x{
207
+ var style_el = document.createElement("style");
208
+ var css = "." + cs + " { " + style + " }";
209
+ style_el.type = "text/css";
210
+ if (style_el.styleSheet){
211
+ style_el.styleSheet.cssText = css;
212
+ } else {
213
+ style_el.appendChild(document.createTextNode(css));
214
+ }
215
+ document.head.appendChild(style_el);
216
+ }
217
+ end
218
+ end
219
+ class React::Component::HyperTestDummy < React::Component::Base
220
+ def render; end
221
+ end
222
+ #{@client_code}
223
+ #{Unparser.unparse(Parser::CurrentRuby.parse(block.source).children.last) if block}
224
+ code
225
+ opts[:code] = Opal.compile(block_with_helpers)
226
+ end
227
+
228
+ component_name ||= 'React::Component::HyperTestDummy'
229
+ ::Rails.cache.write(test_url, [component_name, params, opts])
230
+ test_code_key = "hyper_spec_prerender_test_code.js"
231
+ @@original_server_render_files ||= ::Rails.configuration.react.server_renderer_options[:files]
232
+ if opts[:render_on] == :both || opts[:render_on] == :server_only
233
+ unless opts[:code].blank?
234
+ ::Rails.cache.write(test_code_key, opts[:code])
235
+ ::Rails.configuration.react.server_renderer_options[:files] = @@original_server_render_files + [test_code_key]
236
+ ::React::ServerRendering.reset_pool # make sure contexts are reloaded so they dont use code from cache, as the rails filewatcher doesnt look for cache changes
237
+ else
238
+ ::Rails.cache.delete(test_code_key)
239
+ ::Rails.configuration.react.server_renderer_options[:files] = @@original_server_render_files
240
+ ::React::ServerRendering.reset_pool # make sure contexts are reloaded so they dont use code from cache, as the rails filewatcher doesnt look for cache changes
241
+ end
242
+ end
243
+ Lolex.init(self, client_options[:time_zone], client_options[:clock_resolution])
244
+ visit test_url
245
+ wait_for_ajax unless opts[:no_wait]
246
+ page.instance_variable_set('@hyper_spec_mounted', true)
247
+ end
248
+
249
+ [:callback_history_for, :last_callback_for, :clear_callback_history_for,
250
+ :event_history_for, :last_event_for, :clear_event_history_for].each do |method|
251
+ define_method(method) do |event_name|
252
+ evaluate_ruby("React::TopLevelRailsComponent.#{method}('#{event_name}')")
253
+ end
254
+ end
255
+
256
+ def run_on_client(&block)
257
+ script = Opal.compile(Unparser.unparse(Parser::CurrentRuby.parse(block.source).children.last))
258
+ execute_script(script)
259
+ end
260
+
261
+ def add_class(class_name, style)
262
+ @client_code = "#{@client_code}ComponentHelpers.add_class('#{class_name}', #{style})\n"
263
+ end
264
+
265
+ def open_in_chrome
266
+ if false && ['linux', 'freebsd'].include?(`uname`.downcase)
267
+ `google-chrome http://#{page.server.host}:#{page.server.port}#{page.current_path}`
268
+ else
269
+ `open http://#{page.server.host}:#{page.server.port}#{page.current_path}`
270
+ end
271
+
272
+ while true
273
+ sleep 1.hour
274
+ end
275
+ end
276
+
277
+ def pause(message = nil)
278
+ if message
279
+ puts message
280
+ page.evaluate_ruby "puts #{message.inspect}.to_s + ' (type go() to continue)'"
281
+ end
282
+
283
+ page.evaluate_script('window.hyper_spec_waiting_for_go = true')
284
+
285
+ loop do
286
+ sleep 0.25
287
+ break unless page.evaluate_script('window.hyper_spec_waiting_for_go')
288
+ end
289
+ end
290
+
291
+ def wait_for_size(width, height)
292
+ start_time = Capybara::Helpers.monotonic_time
293
+ stable_count_w = 0
294
+ stable_count_h = 0
295
+ prev_size = [0, 0]
296
+ begin
297
+ sleep 0.05
298
+ curr_size = Capybara.current_session.current_window.size
299
+ return if [width, height] == curr_size
300
+ # some maximum or minimum is reached and size doesnt change anymore
301
+ stable_count_w += 1 if prev_size[0] == curr_size[0]
302
+ stable_count_h += 1 if prev_size[1] == curr_size[1]
303
+ return if stable_count_w > 2 || stable_count_h > 2
304
+ prev_size = curr_size
305
+ end while (Capybara::Helpers.monotonic_time - start_time) < Capybara.current_session.config.default_max_wait_time
306
+ raise Capybara::WindowError, "Window size not stable within #{Capybara.current_session.config.default_max_wait_time} seconds."
307
+ end
308
+
309
+ def size_window(width = nil, height = nil)
310
+ # return if @window_cannot_be_resized
311
+ # original_width = evaluate_script('window.innerWidth')
312
+ # original_height = evaluate_script('window.innerHeight')
313
+ width, height = [height, width] if width == :portrait
314
+ width, height = width if width.is_a? Array
315
+ portrait = true if height == :portrait
316
+
317
+ case width
318
+ when :small
319
+ width, height = [480, 320]
320
+ when :mobile
321
+ width, height = [640, 480]
322
+ when :tablet
323
+ width, height = [960, 640]
324
+ when :large
325
+ width, height = [1920, 6000]
326
+ when :default, nil
327
+ width, height = [1024, 768]
328
+ end
329
+
330
+ width, height = [height, width] if portrait
331
+
332
+ unless RSpec.configuration.debugger_width
333
+ Capybara.current_session.current_window.resize_to(1000, 500)
334
+ wait_for_size(1000, 500)
335
+ inner_width = evaluate_script('window.innerWidth')
336
+ RSpec.configuration.debugger_width = 1000 - inner_width
337
+ end
338
+ Capybara.current_session.current_window
339
+ .resize_to(width + RSpec.configuration.debugger_width, height)
340
+ wait_for_size(width + RSpec.configuration.debugger_width, height)
341
+ end
342
+ end
343
+
344
+ RSpec.configure do |config|
345
+ config.before(:each) do |example|
346
+ ComponentTestHelpers.current_example = example
347
+ ComponentTestHelpers.description_displayed = false
348
+ end
349
+
350
+ if defined?(ActiveRecord)
351
+ config.before(:all) do
352
+ ActiveRecord::Base.class_eval do
353
+ def attributes_on_client(page)
354
+ page.evaluate_ruby("#{self.class.name}.find(#{id}).attributes", symbolize_names: true)
355
+ end
356
+ end
357
+ end
358
+ end
359
+ end
360
+ end