vident 0.7.0 → 0.8.0

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.
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Vident
4
- # Include rake tasks
5
- class Railtie < ::Rails::Railtie
6
- rake_tasks do
7
- load "tasks/vident.rake"
8
- end
9
- end
10
- end
@@ -1,237 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Vident
4
- module RootComponent
5
- module Base
6
- def initialize(
7
- controllers: nil,
8
- actions: nil,
9
- targets: nil,
10
- named_classes: nil, # https://stimulus.hotwired.dev/reference/css-classes
11
- data_maps: nil,
12
- element_tag: nil,
13
- id: nil,
14
- html_options: nil
15
- )
16
- @element_tag = element_tag
17
- @html_options = html_options
18
- @id = id
19
- @controllers = Array.wrap(controllers)
20
- @actions = actions
21
- @targets = targets
22
- @named_classes = named_classes
23
- @data_map_kvs = {}
24
- @data_maps = data_maps
25
- end
26
-
27
- # The view component's helpers for setting stimulus data-* attributes on this component.
28
-
29
- # TODO: rename
30
- # Create a Stimulus action string, and returns it
31
- # examples:
32
- # action(:my_thing) => "current_controller#myThing"
33
- # action(:click, :my_thing) => "click->current_controller#myThing"
34
- # action("click->current_controller#myThing") => "click->current_controller#myThing"
35
- # action("path/to/current", :my_thing) => "path--to--current_controller#myThing"
36
- # action(:click, "path/to/current", :my_thing) => "click->path--to--current_controller#myThing"
37
- def action(*args)
38
- part1, part2, part3 = args
39
- (args.size == 1) ? parse_action_arg(part1) : parse_multiple_action_args(part1, part2, part3)
40
- end
41
-
42
- def action_data_attribute(*actions)
43
- {action: parse_actions(actions).join(" ")}
44
- end
45
-
46
- # TODO: rename & make stimulus Target class instance and returns it, which can convert to String
47
- # Create a Stimulus Target and returns it
48
- # examples:
49
- # target(:my_target) => {controller: 'current_controller' name: 'myTarget'}
50
- # target("path/to/current", :my_target) => {controller: 'path--to--current_controller', name: 'myTarget'}
51
- def target(name, part2 = nil)
52
- if part2.nil?
53
- {controller: implied_controller_name, name: js_name(name)}
54
- else
55
- {controller: stimulize_path(name), name: js_name(part2)}
56
- end
57
- end
58
-
59
- def target_data_attribute(name)
60
- build_target_data_attributes([target(name)])
61
- end
62
-
63
- # Getter for a named classes list so can be used in view to set initial state on SSR
64
- # Returns a String of classes that can be used in a `class` attribute.
65
- def named_classes(*names)
66
- names.map { |name| convert_classes_list_to_string(@named_classes[name]) }.join(" ")
67
- end
68
-
69
- # Helpers for generating the Stimulus data-* attributes directly
70
-
71
- # Return the HTML `data-controller` attribute for the given controllers
72
- def with_controllers(*controllers_to_set)
73
- "data-controller=\"#{controller_list(controllers_to_set)}\"".html_safe
74
- end
75
-
76
- # Return the HTML `data-target` attribute for the given targets
77
- def as_targets(*targets)
78
- attrs = build_target_data_attributes(parse_targets(targets))
79
- attrs.map { |dt, n| "data-#{dt}=\"#{n}\"" }.join(" ").html_safe
80
- end
81
- alias_method :as_target, :as_targets
82
-
83
- # Return the HTML `data-action` attribute for the given actions
84
- def with_actions(*actions_to_set)
85
- "data-action='#{parse_actions(actions_to_set).join(" ")}'".html_safe
86
- end
87
- alias_method :with_action, :with_actions
88
-
89
- private
90
-
91
- # An implicit Stimulus controller name is built from the implicit controller path
92
- def implied_controller_name
93
- stimulize_path(implied_controller_path)
94
- end
95
-
96
- # When using the DSL if you dont specify, the first controller is implied
97
- def implied_controller_path
98
- @controllers&.first || raise(StandardError, "No controllers have been specified")
99
- end
100
-
101
- # A complete list of Stimulus controllers for this component
102
- def controller_list(controllers_to_set)
103
- controllers_to_set&.map { |c| stimulize_path(c) }&.join(" ")
104
- end
105
-
106
- # Complete list of actions ready to be use in the data-action attribute
107
- def action_list(actions_to_parse)
108
- return nil unless actions_to_parse&.size&.positive?
109
- parse_actions(actions_to_parse).join(" ")
110
- end
111
-
112
- # Complete list of targets ready to be use in the data attributes
113
- def target_list
114
- return {} unless @targets&.size&.positive?
115
- build_target_data_attributes(parse_targets(@targets))
116
- end
117
-
118
- def named_classes_list
119
- return {} unless @named_classes&.size&.positive?
120
- build_named_classes_data_attributes(@named_classes)
121
- end
122
-
123
- # stimulus "data-*" attributes map for this component
124
- def tag_data_attributes
125
- {controller: controller_list(@controllers), action: action_list(@actions)}
126
- .merge!(target_list)
127
- .merge!(named_classes_list)
128
- .merge!(data_map_attributes)
129
- .compact_blank!
130
- end
131
-
132
- # Actions can be specified as a symbol, in which case they imply an action on the primary
133
- # controller, or as a string in which case it implies an action that is already fully qualified
134
- # stimulus action.
135
- # 1 Symbol: :my_action => "my_controller#myAction"
136
- # 1 String: "my_controller#myAction"
137
- # 2 Symbols: [:click, :my_action] => "click->my_controller#myAction"
138
- # 1 String, 1 Symbol: ["path/to/controller", :my_action] => "path--to--controller#myAction"
139
- # 1 Symbol, 1 String, 1 Symbol: [:hover, "path/to/controller", :my_action] => "hover->path--to--controller#myAction"
140
-
141
- def parse_action_arg(part1)
142
- if part1.is_a?(Symbol)
143
- # 1 symbol arg, name of method on this controller
144
- "#{implied_controller_name}##{js_name(part1)}"
145
- elsif part1.is_a?(String)
146
- # 1 string arg, fully qualified action
147
- part1
148
- end
149
- end
150
-
151
- def parse_multiple_action_args(part1, part2, part3)
152
- if part3.nil? && part1.is_a?(Symbol)
153
- # 2 symbol args = event + action
154
- "#{part1}->#{implied_controller_name}##{js_name(part2)}"
155
- elsif part3.nil?
156
- # 1 string arg, 1 symbol = controller + action
157
- "#{stimulize_path(part1)}##{js_name(part2)}"
158
- else
159
- # 1 symbol, 1 string, 1 symbol = as above but with event
160
- "#{part1}->#{stimulize_path(part2)}##{js_name(part3)}"
161
- end
162
- end
163
-
164
- # Parse actions, targets and attributes that are passed in as symbols or strings
165
-
166
- def parse_targets(targets)
167
- targets.map { |n| parse_target(n) }
168
- end
169
-
170
- def parse_target(raw_target)
171
- return raw_target if raw_target.is_a?(String)
172
- return raw_target if raw_target.is_a?(Hash)
173
- target(raw_target)
174
- end
175
-
176
- def build_target_data_attributes(targets)
177
- targets.map { |t| ["#{t[:controller]}-target".to_sym, t[:name]] }.to_h
178
- end
179
-
180
- def parse_actions(actions)
181
- actions.map! { |a| a.is_a?(String) ? a : action(*a) }
182
- end
183
-
184
- def parse_attributes(attrs, controller = nil)
185
- attrs.transform_keys { |k| "#{controller || implied_controller_name}-#{k}" }
186
- end
187
-
188
- def data_map_attributes
189
- return {} unless @data_maps
190
- @data_maps.each_with_object({}) do |m, obj|
191
- if m.is_a?(Hash)
192
- obj.merge!(parse_attributes(m))
193
- elsif m.is_a?(Array)
194
- controller_path = m.first
195
- data = m.last
196
- obj.merge!(parse_attributes(data, stimulize_path(controller_path)))
197
- end
198
- end
199
- end
200
-
201
- def parse_named_classes_hash(named_classes)
202
- named_classes.map do |name, classes|
203
- logical_name = name.to_s.dasherize
204
- classes_str = convert_classes_list_to_string(classes)
205
- if classes.is_a?(Hash)
206
- {controller: stimulize_path(classes[:controller_path]), name: logical_name, classes: classes_str}
207
- else
208
- {controller: implied_controller_name, name: logical_name, classes: classes_str}
209
- end
210
- end
211
- end
212
-
213
- def build_named_classes_data_attributes(named_classes)
214
- parse_named_classes_hash(named_classes)
215
- .map { |c| ["#{c[:controller]}-#{c[:name]}-class", c[:classes]] }
216
- .to_h
217
- end
218
-
219
- def convert_classes_list_to_string(classes)
220
- return "" if classes.nil?
221
- return classes if classes.is_a?(String)
222
- return classes.join(" ") if classes.is_a?(Array)
223
- classes[:classes].is_a?(Array) ? classes[:classes].join(" ") : classes[:classes]
224
- end
225
-
226
- # Convert a file path to a stimulus controller name
227
- def stimulize_path(path)
228
- path.split("/").map { |p| p.to_s.dasherize }.join("--")
229
- end
230
-
231
- # Convert a Ruby 'snake case' string to a JavaScript camel case strings
232
- def js_name(name)
233
- name.to_s.camelize(:lower)
234
- end
235
- end
236
- end
237
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if Gem.loaded_specs.has_key? "better_html"
4
- require "better_html"
5
- require "cgi/util"
6
-
7
- module Vident
8
- module RootComponent
9
- module UsingBetterHTML
10
- # Return the HTML `data-controller` attribute for the given controllers
11
- def with_controllers(*controllers_to_set)
12
- helpers.html_attributes("data-controller" => controller_list(controllers_to_set)&.html_safe)
13
- end
14
-
15
- # Return the HTML `data-target` attribute for the given targets
16
- def as_targets(*targets)
17
- attrs = build_target_data_attributes(parse_targets(targets))
18
- helpers.html_attributes(attrs.transform_keys! { |k| "data-#{k}" })
19
- end
20
- alias_method :as_target, :as_targets
21
-
22
- # Return the HTML `data-action` attribute for the given actions
23
- def with_actions(*actions_to_set)
24
- actions_str = action_list(actions_to_set)
25
- actions_str.present? ? helpers.html_attributes("data-action" => actions_str) : nil
26
- end
27
- alias_method :with_action, :with_actions
28
-
29
- private
30
-
31
- # Complete list of actions ready to be use in the data-action attribute
32
- def action_list(actions_to_parse)
33
- return nil unless actions_to_parse&.size&.positive?
34
- # `html_attributes` will escape '->' thus breaking the stimulus action, so we need to do our own escaping
35
- actions_str_raw = parse_actions(actions_to_parse).join(" ")
36
- CGI.escapeHTML(actions_str_raw).gsub("-&gt;", "->").html_safe
37
- end
38
- end
39
- end
40
- end
41
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if Gem.loaded_specs.has_key? "phlex"
4
- require "phlex"
5
-
6
- module Vident
7
- module RootComponent
8
- class UsingPhlexHTML < Phlex::HTML
9
- include Base
10
- if Gem.loaded_specs.has_key? "better_html"
11
- include UsingBetterHTML
12
- end
13
-
14
- STANDARD_ELEMENTS = [:a, :abbr, :address, :article, :aside, :b, :bdi, :bdo, :blockquote, :body, :button, :caption, :cite, :code, :colgroup, :data, :datalist, :dd, :del, :details, :dfn, :dialog, :div, :dl, :dt, :em, :fieldset, :figcaption, :figure, :footer, :form, :g, :h1, :h2, :h3, :h4, :h5, :h6, :head, :header, :hgroup, :html, :i, :iframe, :ins, :kbd, :label, :legend, :li, :main, :map, :mark, :menuitem, :meter, :nav, :noscript, :object, :ol, :optgroup, :option, :output, :p, :path, :picture, :pre, :progress, :q, :rp, :rt, :ruby, :s, :samp, :script, :section, :select, :slot, :small, :span, :strong, :style, :sub, :summary, :sup, :svg, :table, :tbody, :td, :template_tag, :textarea, :tfoot, :th, :thead, :time, :title, :tr, :u, :ul, :video, :wbr].freeze
15
- VOID_ELEMENTS = [:area, :br, :embed, :hr, :img, :input, :link, :meta, :param, :source, :track, :col].freeze
16
-
17
- VALID_TAGS = Set[*(STANDARD_ELEMENTS + VOID_ELEMENTS)].freeze
18
-
19
- # Create a tag for a target with a block containing content
20
- def target_tag(tag_name, targets, **options, &block)
21
- parsed = parse_targets(Array.wrap(targets))
22
- options[:data] ||= {}
23
- options[:data].merge!(build_target_data_attributes(parsed))
24
- generate_tag(tag_name, **options, &block)
25
- end
26
-
27
- # Build a tag with the attributes determined by this components properties and stimulus
28
- # data attributes.
29
- def template(&block)
30
- # Generate tag options and render
31
- tag_type = @element_tag.presence&.to_sym || :div
32
- raise ArgumentError, "Unsupported HTML tag name #{tag_type}" unless VALID_TAGS.include?(tag_type)
33
- options = @html_options&.dup || {}
34
- data_attrs = tag_data_attributes
35
- data_attrs = options[:data].present? ? data_attrs.merge(options[:data]) : data_attrs
36
- options = options.merge(id: @id) if @id
37
- options.except!(:data)
38
- options.merge!(data_attrs.transform_keys { |k| "data-#{k}" })
39
- generate_tag(tag_type, **options, &block)
40
- end
41
-
42
- private
43
-
44
- def generate_tag(tag_type, **options, &block)
45
- send((tag_type == :template) ? :template_tag : tag_type, **options, &block)
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if Gem.loaded_specs.has_key? "view_component"
4
- require "view_component"
5
-
6
- module Vident
7
- module RootComponent
8
- class UsingViewComponent < ::ViewComponent::Base
9
- include Base
10
- if Gem.loaded_specs.has_key? "better_html"
11
- include UsingBetterHTML
12
- end
13
-
14
- SELF_CLOSING_TAGS = Set[:area, :base, :br, :col, :embed, :hr, :img, :input, :link, :meta, :param, :source, :track, :wbr].freeze
15
-
16
- def target_tag(tag_name, targets, **options, &block)
17
- parsed = parse_targets(Array.wrap(targets))
18
- options[:data] ||= {}
19
- options[:data].merge!(build_target_data_attributes(parsed))
20
- content = view_context.capture(&block) if block
21
- view_context.content_tag(tag_name, content, options)
22
- end
23
-
24
- def call
25
- # Generate outer tag options and render
26
- tag_type = content_tag_type
27
- options = content_tag_options
28
- if SELF_CLOSING_TAGS.include?(tag_type)
29
- view_context.tag(tag_type, options)
30
- else
31
- view_context.content_tag(tag_type, content, options)
32
- end
33
- end
34
-
35
- private
36
-
37
- def content_tag_options
38
- options = @html_options&.dup || {}
39
- data_attrs = tag_data_attributes
40
- options[:data] = options[:data].present? ? data_attrs.merge(options[:data]) : data_attrs
41
- return options unless @id
42
- options.merge(id: @id)
43
- end
44
-
45
- def content_tag_type
46
- @element_tag.presence || :div
47
- end
48
- end
49
- end
50
- end
51
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
-
4
- module Vident
5
- module Tailwind
6
- # Adds a utility class merge specifically for Tailwind, allowing you to more easily specify class overrides
7
- # without having to worry about the specificity of the classes.
8
- def produce_style_classes(class_names)
9
- ::TailwindMerge::Merger.new.merge(dedupe_view_component_classes(class_names))
10
- end
11
- end
12
- end
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # TODO: what about when used with Phlex?
4
- module Vident
5
- class TestCase < ::ViewComponent::TestCase
6
- include Vident::Testing::AutoTest
7
- end
8
- end
@@ -1,176 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Vident
4
- module Testing
5
- class AttributesTester
6
- def initialize(test_configurations)
7
- @test_configurations = test_configurations
8
- end
9
-
10
- # Generates attribute hashes for all permutations of the given valid values.
11
- def valid_configurations
12
- # Expands any auto generated values and returns all attributes and their values
13
- test_attrs = prepare_attributes_to_test
14
-
15
- # The first permutation is the initial state
16
- initial_state = prepare_initial_test_state(test_attrs)
17
-
18
- # Create remaining permutations
19
- test_attrs.flat_map do |attr_name, values|
20
- values.map { |v| initial_state.merge(attr_name => v) }
21
- end
22
- end
23
-
24
- # Generates attribute hashes for all permutations of the given invalid values.
25
- def invalid_configurations
26
- return [] unless invalid_configured?
27
-
28
- # prepare a valid initial state, then add any attrs that have :invalid
29
- initial_state = prepare_initial_test_state(prepare_attributes_to_test)
30
-
31
- # Merge in the invalid permutations
32
- test_configurations.inject([]) do |memo, attr|
33
- key, opts = attr
34
- next memo += nil if opts == :strict_boolean
35
- if opts.is_a?(Hash)
36
- values = if opts[:invalid].nil? && opts[:valid].is_a?(Hash)
37
- # If no invalid key specified we generate based on whats valid
38
- config = invalid_attribute_test_values_for(opts[:valid][:type], opts[:valid])
39
- (config&.fetch(:invalid, []) || []) + (config&.fetch(:converts, []) || [])
40
- elsif opts[:invalid].is_a?(Array)
41
- opts[:invalid]
42
- end
43
-
44
- memo += values.map { |i| initial_state.merge(key => i) } if values
45
- end
46
- memo
47
- end
48
- end
49
-
50
- private
51
-
52
- attr_reader :test_configurations
53
-
54
- def invalid_configured?
55
- test_configurations.values.any? { |v| v.respond_to?(:key?) && v.key?(:invalid) }
56
- end
57
-
58
- def prepare_attributes_to_test
59
- test_configurations.transform_values do |attr_config|
60
- next [true, false, nil] if attr_config == :boolean
61
- next [true, false] if attr_config == :strict_boolean
62
- valid = attr_config[:valid]
63
- raise "Ensure :valid attributes configuration is provided" unless valid
64
- next valid if valid.is_a?(Array)
65
- attribute_test_values_for(valid)
66
- end
67
- end
68
-
69
- def prepare_initial_test_state(test_attrs)
70
- initial_state = {}
71
- test_attrs.each { |attr_name, values| initial_state[attr_name] = values.first }
72
- initial_state
73
- end
74
-
75
- def attribute_test_values_for(options)
76
- type = parse_type(options[:type])
77
- return options[:in] if options[:in]
78
- values =
79
- case type
80
- when :string, "String"
81
- s = (1..8).map { |l| ::Faker::String.random(length: l) }
82
- s.prepend "test string"
83
- s = s.select(&:present?)
84
- s << "" if options[:allow_blank]
85
- s
86
- when :boolean
87
- [false, true]
88
- when :float, "Float"
89
- (1..3).map { Faker::Number.positive } + (1..3).map { Faker::Number.negative }
90
- when :numeric, "Numeric"
91
- (1..3).map { Faker::Number.positive } + [1, 5]
92
- when :integer, "Integer"
93
- min = options[:min] || -10_000
94
- max = options[:max] || 10_000
95
- (1..3).map { Kernel.rand(min..max) }
96
- when :array, "Array"
97
- a =
98
- if options[:sub_type] == Numeric
99
- [[1, 2, 3], [0.3, 2, 0.002]]
100
- elsif options[:sub_type]
101
- [[options[:sub_type].new]]
102
- else
103
- [%i[a b c], [1, 2, 3], %w[a b]]
104
- end
105
- a << [] if options[:allow_blank]
106
- a
107
- when :any
108
- [false, 1245, {}, :df, "hi"]
109
- when :hash, "Hash"
110
- a = [{a: 1}]
111
- a << {} if options[:allow_blank]
112
- a
113
- when :symbol, "Symbol"
114
- %i[a b c]
115
- else
116
- raise StandardError, "Attribute type not understood (#{type})"
117
- end
118
-
119
- if options[:allow_nil] || !options[:default].nil? || (options[:allow_blank] && options[:allow_nil].nil?)
120
- values << nil
121
- end
122
- values
123
- end
124
-
125
- def invalid_attribute_test_values_for(type, options)
126
- values = case parse_type(type)
127
- when :string, "String"
128
- # All values are also convertable to string
129
- a = {converts: [false, 1245, 1.0, {}, :df, []], invalid: []}
130
- a[:invalid] << "" if options[:allow_blank] == false
131
- a
132
- when :boolean
133
- # All values are also convertable to boolean with !!
134
- {converts: ["sdf", 234, 3.5, {}, :sdf, []], invalid: []}
135
- when :float, "Float"
136
- # Not all values are convertable to float
137
- {converts: [234, "12.2"], invalid: ["sdf", 234, {}, :sdf, [], false]}
138
- when :numeric, "Numeric"
139
- {converts: ["12.2"], invalid: ["sdf", {}, :sdf, [], false]}
140
- when :integer, "Integer"
141
- # Not all values are convertable to integer
142
- {converts: [234.0, "123", "sdf"], invalid: [{}, :sdf, [], false]}
143
- when :array, "Array"
144
- # Not all values are convertable to array
145
- a = if options[:sub_type]
146
- {converts: [{}, [{}]], invalid: ["sdf", [123], [Class.new]]}
147
- else
148
- {converts: [{}], invalid: ["sdf", 234, 3.5, :sdf, false]}
149
- end
150
- a[:invalid] << [] if options[:allow_blank] == false
151
- a
152
- when :any
153
- # There are no invalid values
154
- {converts: [], invalid: []}
155
- when :hash, "Hash"
156
- a = {converts: [[], [[:a, 1], [:b, 2]]], invalid: [:sdf, false, "boo", 123]}
157
- a[:invalid] << {} if options[:allow_blank] == false
158
- a
159
- when :symbol, "Symbol"
160
- {converts: ["foo"], invalid: [{}, false, [], 123]}
161
- else
162
- raise StandardError, "Attribute type not understood (#{type})"
163
- end
164
-
165
- if options[:default].nil? && (!options[:allow_nil] || (!options[:allow_blank] && options[:allow_nil] != true))
166
- values[:invalid] << nil
167
- end
168
- values
169
- end
170
-
171
- def parse_type(type)
172
- type.is_a?(Symbol) ? type : type.name
173
- end
174
- end
175
- end
176
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "minitest/hooks"
4
-
5
- module Vident
6
- module Testing
7
- module AutoTest
8
- extend ActiveSupport::Concern
9
-
10
- included do
11
- include Minitest::Hooks
12
-
13
- def before_all
14
- @results_content = []
15
- ::Vident::StableId.set_current_sequence_generator
16
- end
17
-
18
- def after_all
19
- html = <<~HTML
20
- <!doctype html>
21
- <html lang="en">
22
- <head>
23
- <meta charset="UTF-8">
24
- <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
25
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
26
- <title>Test run output</title>
27
- </head>
28
- <body>
29
- #{@results_content.map(&:to_s).join("<hr>\n").html_safe}
30
- </body>
31
- </html>
32
- HTML
33
-
34
- # TODO: configurable layout
35
- filename = self.class.name.gsub("::", "_")
36
- File.write("render_results_#{filename}.html", html) # TODO: path for outputs (eg default tmp/)
37
- end
38
- end
39
-
40
- class_methods do
41
- def auto_test(class_under_test, **param_config)
42
- attribute_tester = Vident::Testing::AttributesTester.new(param_config)
43
- attribute_tester.valid_configurations.each_with_index do |test, index|
44
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
45
- def test_renders_with_valid_attrs_#{index}
46
- test_attrs = #{test}
47
- begin
48
- @results_content << render_inline(#{class_under_test}.new(**test_attrs))
49
- rescue => error
50
- assert(false, "Should not raise with #{test.to_s.tr("\"", "'")} but did raise \#{error}")
51
- end
52
- end
53
- RUBY
54
- end
55
-
56
- attribute_tester.invalid_configurations.each_with_index do |test, index|
57
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
58
- def test_raises_with_invalid_attrs_#{index}
59
- test_attrs = #{test}
60
- assert_raises(StandardError, "Should raise with #{test.to_s.tr("\"", "'")}") do
61
- @results_content << render_inline(#{class_under_test}.new(**test_attrs))
62
- end
63
- end
64
- RUBY
65
- end
66
- end
67
- end
68
- end
69
- end
70
- end