hyper-spec 1.0.alpha1.8 → 1.0.0.lap28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|