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.
- checksums.yaml +4 -4
- data/.gitignore +1 -4
- data/.rubocop.yml +107 -0
- data/.travis.yml +15 -20
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +1 -5
- data/LICENSE.txt +21 -0
- data/README.md +380 -0
- data/Rakefile +2 -0
- data/bin/console +0 -0
- data/bin/setup +0 -0
- data/dciy.toml +3 -0
- data/hyper-spec.gemspec +21 -19
- data/lib/hyper-spec.rb +23 -174
- data/lib/hyper-spec/component_test_helpers.rb +360 -0
- data/lib/hyper-spec/{internal/time_cop.rb → time_cop.rb} +2 -14
- data/lib/hyper-spec/version.rb +1 -1
- data/lib/hyper-spec/wait_for_ajax.rb +4 -4
- data/lib/react/top_level_rails_component.rb +94 -0
- metadata +54 -107
- data/lib/hyper-spec/controller_helpers.rb +0 -164
- data/lib/hyper-spec/expectations.rb +0 -64
- data/lib/hyper-spec/helpers.rb +0 -225
- data/lib/hyper-spec/internal/client_execution.rb +0 -94
- data/lib/hyper-spec/internal/component_mount.rb +0 -140
- data/lib/hyper-spec/internal/controller.rb +0 -70
- data/lib/hyper-spec/internal/copy_locals.rb +0 -103
- data/lib/hyper-spec/internal/patches.rb +0 -86
- data/lib/hyper-spec/internal/rails_controller_helpers.rb +0 -50
- data/lib/hyper-spec/internal/window_sizing.rb +0 -73
- data/lib/hyper-spec/rack.rb +0 -67
- data/lib/sources/top_level_rails_component.rb +0 -103
- data/multi_level_how_it_works.md +0 -49
@@ -1,164 +0,0 @@
|
|
1
|
-
module HyperSpec
|
2
|
-
# Defines a series of methods that will build a test page
|
3
|
-
# This module is typically included into the HyperSpecTestController class.
|
4
|
-
module ControllerHelpers
|
5
|
-
# These methods are dependent on the stack being used. See the
|
6
|
-
# RailsControllerHelpers module and rack.rb for two implementations.
|
7
|
-
# Each method should append the appropriate code to the @page array
|
8
|
-
|
9
|
-
# return an empty 204 status either by setting headers or
|
10
|
-
# returning and appropriate response for rack.
|
11
|
-
def ping!
|
12
|
-
raise 'must implement'
|
13
|
-
end
|
14
|
-
|
15
|
-
def json!
|
16
|
-
# this can be a no-op but if json is not included by the application,
|
17
|
-
# hyper-spec will fail, with an error complaining about to_json
|
18
|
-
end
|
19
|
-
|
20
|
-
# return a script or style_sheet tag pointing to some asset.
|
21
|
-
# typically you be pointing to a path on the server or using
|
22
|
-
# sprockets to deliver the file.
|
23
|
-
|
24
|
-
def require!(_file_)
|
25
|
-
raise 'must implement'
|
26
|
-
end
|
27
|
-
|
28
|
-
def style_sheet!(_file_)
|
29
|
-
raise 'must implement'
|
30
|
-
end
|
31
|
-
|
32
|
-
# deliver the page. The @page variable will contain the html ready to go,
|
33
|
-
# any additional options that are passed through from the spec will be
|
34
|
-
# in the @render_params variable. For example layout: 'my_special_layout'
|
35
|
-
|
36
|
-
def deliver!
|
37
|
-
raise 'must implement'
|
38
|
-
end
|
39
|
-
|
40
|
-
# generate a react_render top level block. This will only be called if
|
41
|
-
# you use the mount directive in your specs, so it is optional.
|
42
|
-
|
43
|
-
def mount_component!
|
44
|
-
raise 'mount_component not implemented in HyperSpecTestController'
|
45
|
-
end
|
46
|
-
|
47
|
-
# by default the route back to the controller will be the controller name, less the
|
48
|
-
# word Controller, and underscored. If you want some other name redefine this
|
49
|
-
# method in the HyperSpecController class.
|
50
|
-
|
51
|
-
def self.included(base)
|
52
|
-
def base.route_root
|
53
|
-
# Implement underscore without using rails underscore, so we don't have a
|
54
|
-
# dependency on ActiveSupport
|
55
|
-
name.gsub(/Controller$/, '')
|
56
|
-
.gsub(/::/, '/')
|
57
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
58
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
59
|
-
.tr('-', '_')
|
60
|
-
.downcase
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# The remainder of the methods should work for most implementations.
|
65
|
-
|
66
|
-
# helper method checking the render_on parameter
|
67
|
-
|
68
|
-
def on_client?
|
69
|
-
@render_on != :server_only
|
70
|
-
end
|
71
|
-
|
72
|
-
# The controllers behavior is kept as an array of values in the Controller cache
|
73
|
-
# under a unique id for each test run. Grab the parameters and move them to instance vars
|
74
|
-
|
75
|
-
# If this is just a ping! Then we can just exit with nil.
|
76
|
-
|
77
|
-
def initialize!
|
78
|
-
return if params[:id] == 'ping'
|
79
|
-
|
80
|
-
key = "/#{self.class.route_root}/#{params[:id]}"
|
81
|
-
test_params = Internal::Controller.cache_read(key)
|
82
|
-
|
83
|
-
@component_name = test_params[0]
|
84
|
-
@component_params = test_params[1]
|
85
|
-
@html_block = test_params[2]
|
86
|
-
@render_params = test_params[3]
|
87
|
-
@render_on = @render_params.delete(:render_on) || :client_only
|
88
|
-
@_mock_time = @render_params.delete(:mock_time)
|
89
|
-
@style_sheet = @render_params.delete(:style_sheet)
|
90
|
-
@javascript = @render_params.delete(:javascript)
|
91
|
-
@code = @render_params.delete(:code)
|
92
|
-
|
93
|
-
@page = ['<body>']
|
94
|
-
end
|
95
|
-
|
96
|
-
# add any html code generated by the insert_html directive
|
97
|
-
|
98
|
-
def html_block!
|
99
|
-
@page << @html_block
|
100
|
-
end
|
101
|
-
|
102
|
-
# patch behavior of the HyperComponent TopLevelRailsComponent class
|
103
|
-
# so that things like events are passed back to the test harness
|
104
|
-
TOP_LEVEL_COMPONENT_PATCH =
|
105
|
-
Opal.compile(File.read(File.expand_path('../sources/top_level_rails_component.rb', __dir__)))
|
106
|
-
|
107
|
-
# patch time cop and lolex so they stay in sync across the client and server
|
108
|
-
TIME_COP_CLIENT_PATCH =
|
109
|
-
Opal.compile(File.read(File.expand_path('../hyper-spec/internal/time_cop.rb', __dir__))) +
|
110
|
-
"\n#{File.read(File.expand_path('../sources/lolex.js', __dir__))}"
|
111
|
-
|
112
|
-
def client_code!
|
113
|
-
if @component_name
|
114
|
-
@page << "<script type='text/javascript'>\n#{TOP_LEVEL_COMPONENT_PATCH}\n</script>"
|
115
|
-
end
|
116
|
-
@page << "<script type='text/javascript'>\n#{@code}\n</script>" if @code
|
117
|
-
end
|
118
|
-
|
119
|
-
def time_cop_patch!
|
120
|
-
@page << "<script type='text/javascript'>\n#{TIME_COP_CLIENT_PATCH}\n</script>"
|
121
|
-
end
|
122
|
-
|
123
|
-
# Add the go_function to the client console. This is used to stop a hyper-spec pause directive.
|
124
|
-
|
125
|
-
def go_function!
|
126
|
-
@page << "<script type='text/javascript'>go = function() "\
|
127
|
-
'{window.hyper_spec_waiting_for_go = false}</script>'
|
128
|
-
end
|
129
|
-
|
130
|
-
# First lines displayed on the console will be the name of the spec
|
131
|
-
|
132
|
-
def example_title!
|
133
|
-
title = Internal::Controller.current_example_description!
|
134
|
-
@page << "<script type='text/javascript'>console.log('%c#{title}',"\
|
135
|
-
"'color:green; font-weight:bold; font-size: 200%')</script>"
|
136
|
-
end
|
137
|
-
|
138
|
-
# generate each piece of the page, and then deliver it
|
139
|
-
|
140
|
-
def style_sheet_file
|
141
|
-
@style_sheet || (!@render_params[:layout] && 'application')
|
142
|
-
end
|
143
|
-
|
144
|
-
def application_file
|
145
|
-
@javascript || (on_client? && !@render_params[:layout] && 'application')
|
146
|
-
end
|
147
|
-
|
148
|
-
def test
|
149
|
-
return ping! unless initialize!
|
150
|
-
|
151
|
-
html_block!
|
152
|
-
example_title! if Internal::Controller.current_example
|
153
|
-
go_function! if on_client?
|
154
|
-
style_sheet!(style_sheet_file) if style_sheet_file
|
155
|
-
application!(application_file) if application_file
|
156
|
-
json!
|
157
|
-
time_cop_patch! if on_client? || Lolex.initialized?
|
158
|
-
client_code! if on_client?
|
159
|
-
mount_component! if @component_name
|
160
|
-
@page = @page.join("\n") + "\n</body>\n"
|
161
|
-
deliver!
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
# don't put this in directory lib/rspec/ as that will cause stack overflow with rails/rspec loads
|
2
|
-
module RSpec
|
3
|
-
module Expectations
|
4
|
-
class ExpectationTarget; end
|
5
|
-
module HyperSpecInstanceMethods
|
6
|
-
def self.included(base)
|
7
|
-
base.include HyperSpec::Helpers
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_on_client(matcher, message = nil, &block)
|
11
|
-
evaluate_client.to(matcher, message, &block)
|
12
|
-
end
|
13
|
-
|
14
|
-
alias on_client_to to_on_client
|
15
|
-
alias to_then to_on_client
|
16
|
-
alias then_to to_on_client
|
17
|
-
|
18
|
-
def to_on_client_not(matcher, message = nil, &block)
|
19
|
-
evaluate_client.not_to(matcher, message, &block)
|
20
|
-
end
|
21
|
-
|
22
|
-
alias on_client_to_not to_on_client_not
|
23
|
-
alias on_client_not_to to_on_client_not
|
24
|
-
alias to_not_on_client to_on_client_not
|
25
|
-
alias not_to_on_client to_on_client_not
|
26
|
-
alias then_to_not to_on_client_not
|
27
|
-
alias then_not_to to_on_client_not
|
28
|
-
alias to_not_then to_on_client_not
|
29
|
-
alias not_to_then to_on_client_not
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def evaluate_client
|
34
|
-
source = add_opal_block(@args_str, @target)
|
35
|
-
value = @target.binding.eval("evaluate_ruby(#{source.inspect}, {}, {})")
|
36
|
-
ExpectationTarget.for(value, nil)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class OnClientWithArgsTarget
|
41
|
-
include HyperSpecInstanceMethods
|
42
|
-
|
43
|
-
def initialize(target, args)
|
44
|
-
unless args.is_a? Hash
|
45
|
-
raise ExpectationNotMetError,
|
46
|
-
"You must pass a hash of local var, value pairs to the 'with' modifier"
|
47
|
-
end
|
48
|
-
|
49
|
-
@target = target
|
50
|
-
@args_str = args.collect do |name, value|
|
51
|
-
set_local_var(name, value)
|
52
|
-
end.join("\n")
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
class BlockExpectationTarget < ExpectationTarget
|
57
|
-
include HyperSpecInstanceMethods
|
58
|
-
|
59
|
-
def with(args)
|
60
|
-
OnClientWithArgsTarget.new(@target, args)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
data/lib/hyper-spec/helpers.rb
DELETED
@@ -1,225 +0,0 @@
|
|
1
|
-
module HyperSpec
|
2
|
-
module Helpers
|
3
|
-
include Internal::ClientExecution
|
4
|
-
include Internal::Controller
|
5
|
-
include Internal::ComponentMount
|
6
|
-
include Internal::CopyLocals
|
7
|
-
include Internal::WindowSizing
|
8
|
-
|
9
|
-
##
|
10
|
-
# Mount a component on a page, with a full execution environment
|
11
|
-
# i.e. `mount('MyComponent')` will mount MyComponent on the page.
|
12
|
-
|
13
|
-
# The params argument is a hash of parameters to be passed to the
|
14
|
-
# component.
|
15
|
-
# i.e. `mount('MyComponent', title: 'hello')`
|
16
|
-
|
17
|
-
# The options parameters can set things like:
|
18
|
-
# + controller: the controller class, defaults to HyperSpecTestController
|
19
|
-
# + no_wait: do not wait for any JS to finish executing before proceeding with the spec
|
20
|
-
# + render_on: :client_only (default), :client_and_server, or :server_only
|
21
|
-
# + style_sheet: style sheet file defaults to 'application'
|
22
|
-
# + javascript: javascript file defaults to 'application'
|
23
|
-
# + layout: if provided will use the specified layout otherwise no layout is used
|
24
|
-
# Note that if specifying options the params will have to inclosed in their
|
25
|
-
# own hash.
|
26
|
-
# i.e. `mount('MyComponent', { title: 'hello' }, render_on: :server_only)`
|
27
|
-
# The options can be specified globally using the client_options method (see below.)
|
28
|
-
|
29
|
-
# You may provide a block to mount. This block will be executed on the client
|
30
|
-
# before mounting the component. This is useful for setting up test
|
31
|
-
# components or modifying a components behavior.
|
32
|
-
# i.e.
|
33
|
-
# ```ruby
|
34
|
-
# mount('MyComponent', title: 'hello') do
|
35
|
-
# # this line will be printed on the client console
|
36
|
-
# puts "I'm about to mount my component!"
|
37
|
-
# end
|
38
|
-
# ```
|
39
|
-
def mount(component_name = nil, params = nil, opts = {}, &block)
|
40
|
-
unless params
|
41
|
-
params = opts
|
42
|
-
opts = {}
|
43
|
-
end
|
44
|
-
internal_mount(component_name, params, client_options(opts), &block)
|
45
|
-
end
|
46
|
-
|
47
|
-
##
|
48
|
-
# The following methods retrieve callback and event responses from
|
49
|
-
# the mounted components. The history methods contain the array of all
|
50
|
-
# responses, while last_... returns the last response.
|
51
|
-
# i.e. event_history_for(:save) would return any save events
|
52
|
-
# that the component has raised.
|
53
|
-
|
54
|
-
%i[
|
55
|
-
callback_history_for last_callback_for clear_callback_history_for
|
56
|
-
event_history_for last_event_for clear_event_history_for
|
57
|
-
].each do |method|
|
58
|
-
define_method(method) do |event_name|
|
59
|
-
evaluate_ruby(
|
60
|
-
"Hyperstack::Internal::Component::TopLevelRailsComponent.#{method}('#{event_name}')"
|
61
|
-
)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
##
|
66
|
-
# Define a code block to be prefixed to the mount code.
|
67
|
-
# Useful in before(:each) blocks.
|
68
|
-
|
69
|
-
# In legacy code this was called `on_client`. To get the legacy
|
70
|
-
# behavior alias on_client before_mount
|
71
|
-
# but be aware that on_client is now by default the method
|
72
|
-
# for executing a block of code on the client which was called
|
73
|
-
# evaluate_ruby
|
74
|
-
|
75
|
-
def before_mount(&block)
|
76
|
-
@_hyperspec_private_client_code =
|
77
|
-
"#{@_hyperspec_private_client_code}#{add_opal_block('', block)}"
|
78
|
-
end
|
79
|
-
|
80
|
-
# Execute the block both on the client and on the server. Useful
|
81
|
-
# for mocking isomorphic classes such as ActiveRecord models.
|
82
|
-
|
83
|
-
def isomorphic(&block)
|
84
|
-
yield
|
85
|
-
if page.instance_variable_get('@hyper_spec_mounted')
|
86
|
-
internal_evaluate_ruby(&block)
|
87
|
-
else
|
88
|
-
before_mount(&block)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# Allows options to the mount method to be specified globally
|
93
|
-
|
94
|
-
def client_option(opts = {})
|
95
|
-
@_hyperspec_private_client_options ||= { arity_check: default_arity_check }
|
96
|
-
@_hyperspec_private_client_options.merge! opts
|
97
|
-
build_var_inclusion_lists
|
98
|
-
@_hyperspec_private_client_options
|
99
|
-
end
|
100
|
-
|
101
|
-
def default_arity_check
|
102
|
-
Rails.application.config.opal.arity_check_enabled if defined? Rails
|
103
|
-
rescue StandardError
|
104
|
-
false
|
105
|
-
end
|
106
|
-
|
107
|
-
alias client_options client_option
|
108
|
-
|
109
|
-
##
|
110
|
-
# shorthand for mount with no params (which will effectively reload the page.)
|
111
|
-
# also aliased as reload_page
|
112
|
-
def load_page
|
113
|
-
mount
|
114
|
-
end
|
115
|
-
|
116
|
-
alias reload_page load_page
|
117
|
-
|
118
|
-
##
|
119
|
-
# evaluate a block (or string) on the client
|
120
|
-
# on_client(<optional str>, <opts and/or vars>, &block)
|
121
|
-
#
|
122
|
-
# normal use is to pass a block that will be compiled to the client
|
123
|
-
# but if the ruby code can be supplied as a string in the first arg.
|
124
|
-
|
125
|
-
# opts are passed on to JSON.parse when retrieving the result
|
126
|
-
# from the client.
|
127
|
-
|
128
|
-
# vars is a hash of name: value pairs. Each name will be initialized
|
129
|
-
# as a local variable on the client.
|
130
|
-
|
131
|
-
# example: on_client(x: 12) { x * x } => 144
|
132
|
-
|
133
|
-
# in legacy code on_client was called before_mount
|
134
|
-
# to get legacy on_client behavior you can alias
|
135
|
-
# on_client before_mount
|
136
|
-
|
137
|
-
alias on_client internal_evaluate_ruby
|
138
|
-
|
139
|
-
# attempt to set the window to a particular size
|
140
|
-
|
141
|
-
def size_window(width = nil, height = nil)
|
142
|
-
hs_internal_resize_to(*determine_size(width, height))
|
143
|
-
rescue StandardError
|
144
|
-
true
|
145
|
-
end
|
146
|
-
|
147
|
-
# same signature as on_client, but just returns the compiled
|
148
|
-
# js code. Useful for debugging suspected issues with the
|
149
|
-
# Opal compiler, etc.
|
150
|
-
|
151
|
-
def to_js(*args, &block)
|
152
|
-
opal_compile(*process_params(*args, &block))
|
153
|
-
end
|
154
|
-
|
155
|
-
# legacy methods for backwards compatibility
|
156
|
-
# these may be removed in a future version
|
157
|
-
|
158
|
-
def expect_evaluate_ruby(*args, &block)
|
159
|
-
expect(evaluate_ruby(*args, &block))
|
160
|
-
end
|
161
|
-
|
162
|
-
alias evaluate_ruby internal_evaluate_ruby
|
163
|
-
alias evaluate_promise evaluate_ruby
|
164
|
-
alias expect_promise expect_evaluate_ruby
|
165
|
-
|
166
|
-
def run_on_client(&block)
|
167
|
-
script = opal_compile(Unparser.unparse(Parser::CurrentRuby.parse(block.source).children.last))
|
168
|
-
page.execute_script(script)
|
169
|
-
end
|
170
|
-
|
171
|
-
def insert_html(str)
|
172
|
-
@_hyperspec_private_html_block = "#{@_hyperspec_private_html_block}\n#{str}"
|
173
|
-
end
|
174
|
-
|
175
|
-
def ppr(str)
|
176
|
-
js = opal_compile(str)
|
177
|
-
execute_script("console.log(#{js})")
|
178
|
-
end
|
179
|
-
|
180
|
-
def debugger
|
181
|
-
`debugger`
|
182
|
-
nil
|
183
|
-
end
|
184
|
-
|
185
|
-
def add_class(class_name, style)
|
186
|
-
@_hyperspec_private_client_code =
|
187
|
-
"#{@_hyperspec_private_client_code}ComponentHelpers.add_class('#{class_name}', #{style})\n"
|
188
|
-
end
|
189
|
-
|
190
|
-
def attributes_on_client(model)
|
191
|
-
evaluate_ruby("#{model.class.name}.find(#{model.id}).attributes").symbolize_keys
|
192
|
-
end
|
193
|
-
|
194
|
-
### --- Debugging Helpers ----
|
195
|
-
|
196
|
-
def pause(message = nil)
|
197
|
-
if message
|
198
|
-
puts message
|
199
|
-
internal_evaluate_ruby "puts #{message.inspect}.to_s + ' (type go() to continue)'"
|
200
|
-
end
|
201
|
-
|
202
|
-
page.evaluate_script('window.hyper_spec_waiting_for_go = true')
|
203
|
-
|
204
|
-
loop do
|
205
|
-
sleep 0.25
|
206
|
-
break unless page.evaluate_script('window.hyper_spec_waiting_for_go')
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def open_in_chrome
|
211
|
-
# if ['linux', 'freebsd'].include?(`uname`.downcase)
|
212
|
-
# `google-chrome http://#{page.server.host}:#{page.server.port}#{page.current_path}`
|
213
|
-
# else
|
214
|
-
`open http://#{page.server.host}:#{page.server.port}#{page.current_path}`
|
215
|
-
# end
|
216
|
-
|
217
|
-
loop do
|
218
|
-
sleep 1.hour
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
# short hand for use in pry sessions
|
223
|
-
alias c? internal_evaluate_ruby
|
224
|
-
end
|
225
|
-
end
|