vident 0.7.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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("->", "->").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