vident 0.2.2 → 0.3.1

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 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