vident 0.2.1 → 0.3.0

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: b58f418c48b9c37897a26d83c5b500bacd54c8f37cdb95bf549e5b283c00a36a
4
- data.tar.gz: f2f76b2518a26933cac78eb7c7eb3ab6c0c1091a0e0bca020c8672ed7b947594
3
+ metadata.gz: c2f2d014f440bdd33f9910c04ba79846825afb4c8a61bd6e5079079fd07c0473
4
+ data.tar.gz: fb730836ba6c63a582f97afcaceed12467a1b8ac92f2f5d9a0d871a4791c3de4
5
5
  SHA512:
6
- metadata.gz: ab7baf21555430c664923fdfb1bbfc75a9eb8e56fef39839f1597951c48cf433c89d669898f429dcf87c724ccd5d016d89d06b82193db74dd57f7c3762a93392
7
- data.tar.gz: a93dd215124bd3b6799e8b1519db632ae6521ce091ae9515b6bf943a3fc5987e5cbc301ecf751f778a891ef0ef6c21161103b07978580c4ca2c6430b5d789d48
6
+ metadata.gz: da7e90adb10d5301803cde48f06685983478c01eec44bfe0c8db9a0909734572ccc1ee72490850655bc915f4c2de17d27b1191d3d649c53d7a23d2d1e40b83a2
7
+ data.tar.gz: 2626db1242b53e3abe20c75a8269125e564e1bc89a178f6e76feccda869ef2c41d671d0ece1a48f55441d6e4483e4962c76118ba48beba991529fe2e0ed322cc
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
@@ -33,7 +33,7 @@ module Vident
33
33
  end
34
34
 
35
35
  def identifier_name_path
36
- if ancestors.include?(Phlex::HTML)
36
+ if phlex_component?
37
37
  name.remove("Views::").underscore
38
38
  else
39
39
  name.underscore
@@ -44,6 +44,10 @@ module Vident
44
44
  path.split("/").map { |p| p.to_s.dasherize }.join("--")
45
45
  end
46
46
 
47
+ def phlex_component?
48
+ @phlex_component ||= ancestors.map(&:name).include?("Phlex::HTML")
49
+ end
50
+
47
51
  private
48
52
 
49
53
  # Define reader & presence check method, for performance use ivar directly
@@ -99,7 +103,7 @@ module Vident
99
103
  def parent_element(**options)
100
104
  @parent_element ||= begin
101
105
  # Note: we cant mix phlex and view_component render contexts
102
- klass = if self.class.ancestors.include?(Phlex::HTML)
106
+ klass = if self.class.phlex_component?
103
107
  RootComponent::UsingPhlexHTML
104
108
  else
105
109
  RootComponent::UsingViewComponent
@@ -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
+ 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
+ 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.1"
4
+ VERSION = "0.3.0"
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.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Ierodiaconou
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-26 00:00:00.000000000 Z
11
+ date: 2023-01-05 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