vident 0.2.2 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2279071e93c86a27cab51ac5e30ce0d84ae5696434120043c6a2ebfaab40a4a3
4
- data.tar.gz: 0e85654cb617dfffdfd42c6263f09c41c20a6d527dfb70d74fc71404b97cda13
3
+ metadata.gz: 24963e09fb8ecd09d22f1254eecc8f2d8bf255123e92976f12d224531003b42a
4
+ data.tar.gz: 3b280628d9aa0ee62ad7b92b5ea58e628d3817e74567cfafb4ef22eadedd707e
5
5
  SHA512:
6
- metadata.gz: 127bf0f2146fed5560fac39c40ec15f967ca55e96af800cb128d1524ed91be7aef13078d070de1b06a7801af32d7f79563a583709dbd0a20e0f72913ea9c0c02
7
- data.tar.gz: 75aee42b5f6942857bcbd3267914ec3aa031c9a2bfa3ef2b3489f57fde5d172f75508a17b8ce6a89a95791a2b270d182e1194906a1e21eee818ffdc8068f3c70
6
+ metadata.gz: fe03e658c1d0ca0e537b66cf465c9628566507053aaf7fbd2d03125a98c1f5f829a1a7c51fb2751314aa38648f3119337ebfa2df932d462dd4e2ab31a235b730
7
+ data.tar.gz: 100fb3cbe9fce6cb5083ebd95237796f57d73f44a02385e274d0e433763c0037cbad8d257994aa059e5ab6e137fb781309ea24f2cf277e22ec59f79d3f370343
data/Gemfile CHANGED
@@ -27,5 +27,7 @@ gem "sqlite3"
27
27
  gem "rake", "~> 13.0"
28
28
 
29
29
  gem "minitest", "~> 5.0"
30
+ gem "minitest-hooks"
31
+ gem "faker"
30
32
 
31
33
  gem "standard", "~> 1.3"
data/lib/vident/base.rb CHANGED
@@ -29,7 +29,7 @@ module Vident
29
29
 
30
30
  # stimulus controller identifier
31
31
  def stimulus_identifier
32
- stimulus_identifier_from_path(identifier_name_path)
32
+ ::Vident::Base.stimulus_identifier_from_path(identifier_name_path)
33
33
  end
34
34
 
35
35
  def identifier_name_path
@@ -40,10 +40,6 @@ module Vident
40
40
  end
41
41
  end
42
42
 
43
- def stimulus_identifier_from_path(path)
44
- path.split("/").map { |p| p.to_s.dasherize }.join("--")
45
- end
46
-
47
43
  def phlex_component?
48
44
  @phlex_component ||= ancestors.map(&:name).include?("Phlex::HTML")
49
45
  end
@@ -147,6 +143,11 @@ module Vident
147
143
  self.class.identifier_name_path
148
144
  end
149
145
 
146
+ def stimulus_identifier_from_path(path)
147
+ path.split("/").map { |p| p.to_s.dasherize }.join("--")
148
+ end
149
+ module_function :stimulus_identifier_from_path
150
+
150
151
  protected
151
152
 
152
153
  # Prepare the stimulus attributes for a StimulusComponent
@@ -100,6 +100,9 @@ module Vident
100
100
  else
101
101
  @cache_key ||= {}
102
102
  end
103
+
104
+ # TODO: remove the benchmarking code here
105
+
103
106
  if @enable_cache_key_benchmarking
104
107
  time = ::Benchmark.measure { generate_cache_key(n) }
105
108
  ::Logging::Log.debug "Cache key #{self.class.name}: #{time.real}"
@@ -0,0 +1,8 @@
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
@@ -0,0 +1,176 @@
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
@@ -0,0 +1,70 @@
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vident
4
- VERSION = "0.2.2"
4
+ VERSION = "0.3.1"
5
5
  end
data/lib/vident.rb CHANGED
@@ -32,3 +32,9 @@ require_relative "vident/base"
32
32
  require_relative "vident/component"
33
33
  require_relative "vident/typed_component"
34
34
  require_relative "vident/caching/cache_key"
35
+
36
+ require_relative "vident/testing/attributes_tester"
37
+ require_relative "vident/testing/auto_test"
38
+
39
+ # TODO: what if not using view_component?
40
+ require_relative "vident/test_case"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vident
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Ierodiaconou
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-04 00:00:00.000000000 Z
11
+ date: 2023-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -60,6 +60,9 @@ files:
60
60
  - lib/vident/root_component/using_phlex_html.rb
61
61
  - lib/vident/root_component/using_view_component.rb
62
62
  - lib/vident/stable_id.rb
63
+ - lib/vident/test_case.rb
64
+ - lib/vident/testing/attributes_tester.rb
65
+ - lib/vident/testing/auto_test.rb
63
66
  - lib/vident/typed_component.rb
64
67
  - lib/vident/version.rb
65
68
  - sig/vident.rbs